Add sources for API 34

https://dl.google.com/android/repository/sources-34_r01.zip

Test: None
Change-Id: I254306ce746dcadecd8f756a445c667d8fecbd2a
diff --git a/android-34/android/os/AggregateBatteryConsumer.java b/android-34/android/os/AggregateBatteryConsumer.java
new file mode 100644
index 0000000..7a153ef
--- /dev/null
+++ b/android-34/android/os/AggregateBatteryConsumer.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.NonNull;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Contains power consumption data across the entire device.
+ *
+ * {@hide}
+ */
+public final class AggregateBatteryConsumer extends BatteryConsumer {
+    static final int CONSUMER_TYPE_AGGREGATE = 0;
+
+    static final int COLUMN_INDEX_SCOPE = BatteryConsumer.COLUMN_COUNT;
+    static final int COLUMN_INDEX_CONSUMED_POWER = COLUMN_INDEX_SCOPE + 1;
+    static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 2;
+
+    AggregateBatteryConsumer(BatteryConsumerData data) {
+        super(data);
+    }
+
+    private AggregateBatteryConsumer(@NonNull Builder builder) {
+        super(builder.mData, builder.mPowerComponentsBuilder.build());
+    }
+
+    int getScope() {
+        return mData.getInt(COLUMN_INDEX_SCOPE);
+    }
+
+    @Override
+    public void dump(PrintWriter pw, boolean skipEmptyComponents) {
+        mPowerComponents.dump(pw, skipEmptyComponents);
+    }
+
+    @Override
+    public double getConsumedPower() {
+        return mData.getDouble(COLUMN_INDEX_CONSUMED_POWER);
+    }
+
+    /** Serializes this object to XML */
+    void writeToXml(TypedXmlSerializer serializer,
+            @BatteryUsageStats.AggregateBatteryConsumerScope int scope) throws IOException {
+        serializer.startTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+        serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE, scope);
+        serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, getConsumedPower());
+        mPowerComponents.writeToXml(serializer);
+        serializer.endTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+    }
+
+    /** Parses an XML representation and populates the BatteryUsageStats builder */
+    static void parseXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+            throws XmlPullParserException, IOException {
+        final int scope = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE);
+        final Builder consumerBuilder = builder.getAggregateBatteryConsumerBuilder(scope);
+
+        int eventType = parser.getEventType();
+        if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+                BatteryUsageStats.XML_TAG_AGGREGATE)) {
+            throw new XmlPullParserException("Invalid XML parser state");
+        }
+
+        consumerBuilder.setConsumedPower(
+                parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER));
+
+        while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+                BatteryUsageStats.XML_TAG_AGGREGATE))
+                && eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+                    PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+                }
+            }
+            eventType = parser.next();
+        }
+    }
+
+    void writePowerComponentModelProto(@NonNull ProtoOutputStream proto) {
+        for (int i = 0; i < POWER_COMPONENT_COUNT; i++) {
+            final int powerModel = getPowerModel(i);
+            if (powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) continue;
+
+            final long token = proto.start(BatteryUsageStatsAtomsProto.COMPONENT_MODELS);
+            proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.COMPONENT, i);
+            proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_MODEL,
+                    powerModelToProtoEnum(powerModel));
+            proto.end(token);
+        }
+    }
+
+    /**
+     * Builder for DeviceBatteryConsumer.
+     */
+    public static final class Builder extends BaseBuilder<AggregateBatteryConsumer.Builder> {
+        public Builder(BatteryConsumer.BatteryConsumerData data, int scope) {
+            super(data, CONSUMER_TYPE_AGGREGATE);
+            data.putInt(COLUMN_INDEX_SCOPE, scope);
+        }
+
+        /**
+         * Sets the total power included in this aggregate.
+         */
+        public Builder setConsumedPower(double consumedPowerMah) {
+            mData.putDouble(COLUMN_INDEX_CONSUMED_POWER, consumedPowerMah);
+            return this;
+        }
+
+        /**
+         * Adds power and usage duration from the supplied AggregateBatteryConsumer.
+         */
+        public void add(AggregateBatteryConsumer aggregateBatteryConsumer) {
+            setConsumedPower(mData.getDouble(COLUMN_INDEX_CONSUMED_POWER)
+                    + aggregateBatteryConsumer.getConsumedPower());
+            mPowerComponentsBuilder.addPowerAndDuration(aggregateBatteryConsumer.mPowerComponents);
+        }
+
+        /**
+         * Creates a read-only object out of the Builder values.
+         */
+        @NonNull
+        public AggregateBatteryConsumer build() {
+            return new AggregateBatteryConsumer(this);
+        }
+    }
+}
diff --git a/android-34/android/os/AppZygote.java b/android-34/android/os/AppZygote.java
new file mode 100644
index 0000000..07fbe4a
--- /dev/null
+++ b/android-34/android/os/AppZygote.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProcessInfo;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.Zygote;
+
+import dalvik.system.VMRuntime;
+
+/**
+ * AppZygote is responsible for interfacing with an application-specific zygote.
+ *
+ * Application zygotes can pre-load app-specific code and data, and this interface can
+ * be used to spawn isolated services from such an application zygote.
+ *
+ * Note that we'll have only one instance of this per application / uid combination.
+ *
+ * @hide
+ */
+public class AppZygote {
+    private static final String LOG_TAG = "AppZygote";
+
+    // UID of the Zygote itself
+    private final int mZygoteUid;
+
+    // First UID/GID of the range the AppZygote can setuid()/setgid() to
+    private final int mZygoteUidGidMin;
+
+    // Last UID/GID of the range the AppZygote can setuid()/setgid() to
+    private final int mZygoteUidGidMax;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Instance that maintains the socket connection to the zygote. This is {@code null} if the
+     * zygote is not running or is not connected.
+     */
+    @GuardedBy("mLock")
+    private ChildZygoteProcess mZygote;
+
+    private final ApplicationInfo mAppInfo;
+    private final ProcessInfo mProcessInfo;
+
+    public AppZygote(ApplicationInfo appInfo, ProcessInfo processInfo, int zygoteUid, int uidGidMin,
+            int uidGidMax) {
+        mAppInfo = appInfo;
+        mProcessInfo = processInfo;
+        mZygoteUid = zygoteUid;
+        mZygoteUidGidMin = uidGidMin;
+        mZygoteUidGidMax = uidGidMax;
+    }
+
+    /**
+     * Returns the zygote process associated with this app zygote.
+     * Creates the process if it's not already running.
+     */
+    public ChildZygoteProcess getProcess() {
+        synchronized (mLock) {
+            if (mZygote != null) return mZygote;
+
+            connectToZygoteIfNeededLocked();
+            return mZygote;
+        }
+    }
+
+    /**
+     * Stops the Zygote and kills the zygote process.
+     */
+    public void stopZygote() {
+        synchronized (mLock) {
+            stopZygoteLocked();
+        }
+    }
+
+    public ApplicationInfo getAppInfo() {
+        return mAppInfo;
+    }
+
+    @GuardedBy("mLock")
+    private void stopZygoteLocked() {
+        if (mZygote != null) {
+            mZygote.close();
+            // use killProcessGroup() here, so we kill all untracked children as well.
+            Process.killProcessGroup(mZygoteUid, mZygote.getPid());
+            mZygote = null;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void connectToZygoteIfNeededLocked() {
+        String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi :
+                Build.SUPPORTED_ABIS[0];
+        try {
+            int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote(
+                    mAppInfo, mProcessInfo);
+            mZygote = Process.ZYGOTE_PROCESS.startChildZygote(
+                    "com.android.internal.os.AppZygoteInit",
+                    mAppInfo.processName + "_zygote",
+                    mZygoteUid,
+                    mZygoteUid,
+                    null,  // gids
+                    runtimeFlags,
+                    "app_zygote",  // seInfo
+                    abi,  // abi
+                    abi, // acceptedAbiList
+                    VMRuntime.getInstructionSet(abi), // instructionSet
+                    mZygoteUidGidMin,
+                    mZygoteUidGidMax);
+
+            ZygoteProcess.waitForConnectionToZygote(mZygote.getPrimarySocketAddress());
+            // preload application code in the zygote
+            Log.i(LOG_TAG, "Starting application preload.");
+            mZygote.preloadApp(mAppInfo, abi);
+            Log.i(LOG_TAG, "Application preload done.");
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Error connecting to app zygote", e);
+            stopZygoteLocked();
+        }
+    }
+}
diff --git a/android-34/android/os/ArtModuleServiceManager.java b/android-34/android/os/ArtModuleServiceManager.java
new file mode 100644
index 0000000..0009e61
--- /dev/null
+++ b/android-34/android/os/ArtModuleServiceManager.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the ART
+ * mainline module.
+ *
+ * Only the ART mainline module will be able to access an instance of this class.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class ArtModuleServiceManager {
+    /** @hide */
+    public ArtModuleServiceManager() {}
+
+    /** A class that exposes the method to obtain each system service. */
+    public static final class ServiceRegisterer {
+        @NonNull private final String mServiceName;
+
+        /** @hide */
+        public ServiceRegisterer(@NonNull String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Returns the service from the service manager.
+         *
+         * If the service is not running, servicemanager will attempt to start it, and this function
+         * will wait for it to be ready.
+         *
+         * @return {@code null} only if there are permission problems or fatal errors.
+         */
+        @Nullable
+        public IBinder waitForService() {
+            return ServiceManager.waitForService(mServiceName);
+        }
+    }
+
+    /** Returns {@link ServiceRegisterer} for the "artd" service. */
+    @NonNull
+    public ServiceRegisterer getArtdServiceRegisterer() {
+        return new ServiceRegisterer("artd");
+    }
+}
diff --git a/android-34/android/os/AsyncResult.java b/android-34/android/os/AsyncResult.java
new file mode 100644
index 0000000..e80528b
--- /dev/null
+++ b/android-34/android/os/AsyncResult.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+/** @hide */
+public class AsyncResult
+{
+
+    /*************************** Instance Variables **************************/
+
+    // Expect either exception or result to be null
+    @UnsupportedAppUsage
+    public Object userObj;
+    @UnsupportedAppUsage
+    public Throwable exception;
+    @UnsupportedAppUsage
+    public Object result;
+
+    /***************************** Class Methods *****************************/
+
+    /** Saves and sets m.obj */
+    @UnsupportedAppUsage
+    public static AsyncResult 
+    forMessage(Message m, Object r, Throwable ex)
+    {
+        AsyncResult ret;
+
+        ret = new AsyncResult (m.obj, r, ex);
+
+        m.obj = ret; 
+
+        return ret;
+    }
+
+    /** Saves and sets m.obj */
+    @UnsupportedAppUsage
+    public static AsyncResult 
+    forMessage(Message m)
+    {
+        AsyncResult ret;
+
+        ret = new AsyncResult (m.obj, null, null);
+
+        m.obj = ret; 
+
+        return ret;
+    }
+
+    /** please note, this sets m.obj to be this */
+    @UnsupportedAppUsage
+    public 
+    AsyncResult (Object uo, Object r, Throwable ex)
+    {
+        userObj = uo;
+        result = r;
+        exception = ex;
+    }
+}
diff --git a/android-34/android/os/AsyncTask.java b/android-34/android/os/AsyncTask.java
new file mode 100644
index 0000000..e1dabd3
--- /dev/null
+++ b/android-34/android/os/AsyncTask.java
@@ -0,0 +1,811 @@
+/*
+ * 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.
+ * 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.os;
+
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import java.util.ArrayDeque;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * <p>AsyncTask was intended to enable proper and easy use of the UI thread. However, the most
+ * common use case was for integrating into UI, and that would cause Context leaks, missed
+ * callbacks, or crashes on configuration changes. It also has inconsistent behavior on different
+ * versions of the platform, swallows exceptions from {@code doInBackground}, and does not provide
+ * much utility over using {@link Executor}s directly.</p>
+ *
+ * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
+ * and does not constitute a generic threading framework. AsyncTasks should ideally be
+ * used for short operations (a few seconds at the most.) If you need to keep threads
+ * running for long periods of time, it is highly recommended you use the various APIs
+ * provided by the <code>java.util.concurrent</code> package such as {@link Executor},
+ * {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
+ *
+ * <p>An asynchronous task is defined by a computation that runs on a background thread and
+ * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
+ * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
+ * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
+ * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using tasks and threads, read the
+ * <a href="{@docRoot}guide/components/processes-and-threads.html">Processes and
+ * Threads</a> developer guide.</p>
+ * </div>
+ *
+ * <h2>Usage</h2>
+ * <p>AsyncTask must be subclassed to be used. The subclass will override at least
+ * one method ({@link #doInBackground}), and most often will override a
+ * second one ({@link #onPostExecute}.)</p>
+ *
+ * <p>Here is an example of subclassing:</p>
+ * <pre class="prettyprint">
+ * private class DownloadFilesTask extends AsyncTask&lt;URL, Integer, Long&gt; {
+ *     protected Long doInBackground(URL... urls) {
+ *         int count = urls.length;
+ *         long totalSize = 0;
+ *         for (int i = 0; i &lt; count; i++) {
+ *             totalSize += Downloader.downloadFile(urls[i]);
+ *             publishProgress((int) ((i / (float) count) * 100));
+ *             // Escape early if cancel() is called
+ *             if (isCancelled()) break;
+ *         }
+ *         return totalSize;
+ *     }
+ *
+ *     protected void onProgressUpdate(Integer... progress) {
+ *         setProgressPercent(progress[0]);
+ *     }
+ *
+ *     protected void onPostExecute(Long result) {
+ *         showDialog("Downloaded " + result + " bytes");
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>Once created, a task is executed very simply:</p>
+ * <pre class="prettyprint">
+ * new DownloadFilesTask().execute(url1, url2, url3);
+ * </pre>
+ *
+ * <h2>AsyncTask's generic types</h2>
+ * <p>The three types used by an asynchronous task are the following:</p>
+ * <ol>
+ *     <li><code>Params</code>, the type of the parameters sent to the task upon
+ *     execution.</li>
+ *     <li><code>Progress</code>, the type of the progress units published during
+ *     the background computation.</li>
+ *     <li><code>Result</code>, the type of the result of the background
+ *     computation.</li>
+ * </ol>
+ * <p>Not all types are always used by an asynchronous task. To mark a type as unused,
+ * simply use the type {@link Void}:</p>
+ * <pre>
+ * private class MyTask extends AsyncTask&lt;Void, Void, Void&gt; { ... }
+ * </pre>
+ *
+ * <h2>The 4 steps</h2>
+ * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
+ * <ol>
+ *     <li>{@link #onPreExecute()}, invoked on the UI thread before the task
+ *     is executed. This step is normally used to setup the task, for instance by
+ *     showing a progress bar in the user interface.</li>
+ *     <li>{@link #doInBackground}, invoked on the background thread
+ *     immediately after {@link #onPreExecute()} finishes executing. This step is used
+ *     to perform background computation that can take a long time. The parameters
+ *     of the asynchronous task are passed to this step. The result of the computation must
+ *     be returned by this step and will be passed back to the last step. This step
+ *     can also use {@link #publishProgress} to publish one or more units
+ *     of progress. These values are published on the UI thread, in the
+ *     {@link #onProgressUpdate} step.</li>
+ *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
+ *     call to {@link #publishProgress}. The timing of the execution is
+ *     undefined. This method is used to display any form of progress in the user
+ *     interface while the background computation is still executing. For instance,
+ *     it can be used to animate a progress bar or show logs in a text field.</li>
+ *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
+ *     computation finishes. The result of the background computation is passed to
+ *     this step as a parameter.</li>
+ * </ol>
+ * 
+ * <h2>Cancelling a task</h2>
+ * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
+ * this method will cause subsequent calls to {@link #isCancelled()} to return true.
+ * After invoking this method, {@link #onCancelled(Object)}, instead of
+ * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
+ * returns. To ensure that a task is cancelled as quickly as possible, you should always
+ * check the return value of {@link #isCancelled()} periodically from
+ * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
+ *
+ * <h2>Threading rules</h2>
+ * <p>There are a few threading rules that must be followed for this class to
+ * work properly:</p>
+ * <ul>
+ *     <li>The AsyncTask class must be loaded on the UI thread. This is done
+ *     automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
+ *     <li>The task instance must be created on the UI thread.</li>
+ *     <li>{@link #execute} must be invoked on the UI thread.</li>
+ *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
+ *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
+ *     <li>The task can be executed only once (an exception will be thrown if
+ *     a second execution is attempted.)</li>
+ * </ul>
+ *
+ * <h2>Memory observability</h2>
+ * <p>AsyncTask guarantees that all callback calls are synchronized to ensure the following
+ * without explicit synchronizations.</p>
+ * <ul>
+ *     <li>The memory effects of {@link #onPreExecute}, and anything else
+ *     executed before the call to {@link #execute}, including the construction
+ *     of the AsyncTask object, are visible to {@link #doInBackground}.
+ *     <li>The memory effects of {@link #doInBackground} are visible to
+ *     {@link #onPostExecute}.
+ *     <li>Any memory effects of {@link #doInBackground} preceding a call
+ *     to {@link #publishProgress} are visible to the corresponding
+ *     {@link #onProgressUpdate} call. (But {@link #doInBackground} continues to
+ *     run, and care needs to be taken that later updates in {@link #doInBackground}
+ *     do not interfere with an in-progress {@link #onProgressUpdate} call.)
+ *     <li>Any memory effects preceding a call to {@link #cancel} are visible
+ *     after a call to {@link #isCancelled} that returns true as a result, or
+ *     during and after a resulting call to {@link #onCancelled}.
+ * </ul>
+ *
+ * <h2>Order of execution</h2>
+ * <p>When first introduced, AsyncTasks were executed serially on a single background
+ * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
+ * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
+ * thread to avoid common application errors caused by parallel execution.</p>
+ * <p>If you truly want parallel execution, you can invoke
+ * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
+ * {@link #THREAD_POOL_EXECUTOR}.</p>
+ *
+ * @deprecated Use the standard <code>java.util.concurrent</code> or
+ *   <a href="https://developer.android.com/topic/libraries/architecture/coroutines">
+ *   Kotlin concurrency utilities</a> instead.
+ */
+@Deprecated
+public abstract class AsyncTask<Params, Progress, Result> {
+    private static final String LOG_TAG = "AsyncTask";
+
+    // We keep only a single pool thread around all the time.
+    // We let the pool grow to a fairly large number of threads if necessary,
+    // but let them time out quickly. In the unlikely case that we run out of threads,
+    // we fall back to a simple unbounded-queue executor.
+    // This combination ensures that:
+    // 1. We normally keep few threads (1) around.
+    // 2. We queue only after launching a significantly larger, but still bounded, set of threads.
+    // 3. We keep the total number of threads bounded, but still allow an unbounded set
+    //    of tasks to be queued.
+    private static final int CORE_POOL_SIZE = 1;
+    private static final int MAXIMUM_POOL_SIZE = 20;
+    private static final int BACKUP_POOL_SIZE = 5;
+    private static final int KEEP_ALIVE_SECONDS = 3;
+
+    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+        private final AtomicInteger mCount = new AtomicInteger(1);
+
+        public Thread newThread(Runnable r) {
+            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
+        }
+    };
+
+    // Used only for rejected executions.
+    // Initialization protected by sRunOnSerialPolicy lock.
+    private static ThreadPoolExecutor sBackupExecutor;
+    private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue;
+
+    private static final RejectedExecutionHandler sRunOnSerialPolicy =
+            new RejectedExecutionHandler() {
+        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
+            android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size");
+            // As a last ditch fallback, run it on an executor with an unbounded queue.
+            // Create this executor lazily, hopefully almost never.
+            synchronized (this) {
+                if (sBackupExecutor == null) {
+                    sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
+                    sBackupExecutor = new ThreadPoolExecutor(
+                            BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
+                            TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
+                    sBackupExecutor.allowCoreThreadTimeOut(true);
+                }
+            }
+            sBackupExecutor.execute(r);
+        }
+    };
+
+    /**
+     * An {@link Executor} that can be used to execute tasks in parallel.
+     *
+     * @deprecated Using a single thread pool for a general purpose results in suboptimal behavior
+     *   for different tasks. Small, CPU-bound tasks benefit from a bounded pool and queueing, and
+     *   long-running blocking tasks, such as network operations, benefit from many threads. Use or
+     *   create an {@link Executor} configured for your use case.
+     */
+    @Deprecated
+    public static final Executor THREAD_POOL_EXECUTOR;
+
+    static {
+        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
+                new SynchronousQueue<Runnable>(), sThreadFactory);
+        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
+        THREAD_POOL_EXECUTOR = threadPoolExecutor;
+    }
+
+    /**
+     * An {@link Executor} that executes tasks one at a time in serial
+     * order.  This serialization is global to a particular process.
+     *
+     * @deprecated Globally serializing tasks results in excessive queuing for unrelated operations.
+     */
+    @Deprecated
+    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
+
+    private static final int MESSAGE_POST_RESULT = 0x1;
+    private static final int MESSAGE_POST_PROGRESS = 0x2;
+
+    @UnsupportedAppUsage
+    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
+    private static InternalHandler sHandler;
+
+    @UnsupportedAppUsage
+    private final WorkerRunnable<Params, Result> mWorker;
+    @UnsupportedAppUsage
+    private final FutureTask<Result> mFuture;
+
+    @UnsupportedAppUsage
+    private volatile Status mStatus = Status.PENDING;
+    
+    private final AtomicBoolean mCancelled = new AtomicBoolean();
+    @UnsupportedAppUsage
+    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
+
+    private final Handler mHandler;
+
+    private static class SerialExecutor implements Executor {
+        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
+        Runnable mActive;
+
+        public synchronized void execute(final Runnable r) {
+            mTasks.offer(new Runnable() {
+                public void run() {
+                    try {
+                        r.run();
+                    } finally {
+                        scheduleNext();
+                    }
+                }
+            });
+            if (mActive == null) {
+                scheduleNext();
+            }
+        }
+
+        protected synchronized void scheduleNext() {
+            if ((mActive = mTasks.poll()) != null) {
+                THREAD_POOL_EXECUTOR.execute(mActive);
+            }
+        }
+    }
+
+    /**
+     * Indicates the current status of the task. Each status will be set only once
+     * during the lifetime of a task.
+     */
+    public enum Status {
+        /**
+         * Indicates that the task has not been executed yet.
+         */
+        PENDING,
+        /**
+         * Indicates that the task is running.
+         */
+        RUNNING,
+        /**
+         * Indicates that {@link AsyncTask#onPostExecute} has finished.
+         */
+        FINISHED,
+    }
+
+    private static Handler getMainHandler() {
+        synchronized (AsyncTask.class) {
+            if (sHandler == null) {
+                sHandler = new InternalHandler(Looper.getMainLooper());
+            }
+            return sHandler;
+        }
+    }
+
+    private Handler getHandler() {
+        return mHandler;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static void setDefaultExecutor(Executor exec) {
+        sDefaultExecutor = exec;
+    }
+
+    /**
+     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+     */
+    public AsyncTask() {
+        this((Looper) null);
+    }
+
+    /**
+     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+     *
+     * @hide
+     */
+    public AsyncTask(@Nullable Handler handler) {
+        this(handler != null ? handler.getLooper() : null);
+    }
+
+    /**
+     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+     *
+     * @hide
+     */
+    public AsyncTask(@Nullable Looper callbackLooper) {
+        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
+            ? getMainHandler()
+            : new Handler(callbackLooper);
+
+        mWorker = new WorkerRunnable<Params, Result>() {
+            public Result call() throws Exception {
+                mTaskInvoked.set(true);
+                Result result = null;
+                try {
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                    //noinspection unchecked
+                    result = doInBackground(mParams);
+                    Binder.flushPendingCommands();
+                } catch (Throwable tr) {
+                    mCancelled.set(true);
+                    throw tr;
+                } finally {
+                    postResult(result);
+                }
+                return result;
+            }
+        };
+
+        mFuture = new FutureTask<Result>(mWorker) {
+            @Override
+            protected void done() {
+                try {
+                    postResultIfNotInvoked(get());
+                } catch (InterruptedException e) {
+                    android.util.Log.w(LOG_TAG, e);
+                } catch (ExecutionException e) {
+                    throw new RuntimeException("An error occurred while executing doInBackground()",
+                            e.getCause());
+                } catch (CancellationException e) {
+                    postResultIfNotInvoked(null);
+                }
+            }
+        };
+    }
+
+    private void postResultIfNotInvoked(Result result) {
+        final boolean wasTaskInvoked = mTaskInvoked.get();
+        if (!wasTaskInvoked) {
+            postResult(result);
+        }
+    }
+
+    private Result postResult(Result result) {
+        @SuppressWarnings("unchecked")
+        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
+                new AsyncTaskResult<Result>(this, result));
+        message.sendToTarget();
+        return result;
+    }
+
+    /**
+     * Returns the current status of this task.
+     *
+     * @return The current status.
+     */
+    public final Status getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Override this method to perform a computation on a background thread. The
+     * specified parameters are the parameters passed to {@link #execute}
+     * by the caller of this task.
+     *
+     * This will normally run on a background thread. But to better
+     * support testing frameworks, it is recommended that this also tolerates
+     * direct execution on the foreground thread, as part of the {@link #execute} call.
+     *
+     * This method can call {@link #publishProgress} to publish updates
+     * on the UI thread.
+     *
+     * @param params The parameters of the task.
+     *
+     * @return A result, defined by the subclass of this task.
+     *
+     * @see #onPreExecute()
+     * @see #onPostExecute
+     * @see #publishProgress
+     */
+    @WorkerThread
+    protected abstract Result doInBackground(Params... params);
+
+    /**
+     * Runs on the UI thread before {@link #doInBackground}.
+     * Invoked directly by {@link #execute} or {@link #executeOnExecutor}.
+     * The default version does nothing.
+     *
+     * @see #onPostExecute
+     * @see #doInBackground
+     */
+    @MainThread
+    protected void onPreExecute() {
+    }
+
+    /**
+     * <p>Runs on the UI thread after {@link #doInBackground}. The
+     * specified result is the value returned by {@link #doInBackground}.
+     * To better support testing frameworks, it is recommended that this be
+     * written to tolerate direct execution as part of the execute() call.
+     * The default version does nothing.</p>
+     * 
+     * <p>This method won't be invoked if the task was cancelled.</p>
+     *
+     * @param result The result of the operation computed by {@link #doInBackground}.
+     *
+     * @see #onPreExecute
+     * @see #doInBackground
+     * @see #onCancelled(Object) 
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    @MainThread
+    protected void onPostExecute(Result result) {
+    }
+
+    /**
+     * Runs on the UI thread after {@link #publishProgress} is invoked.
+     * The specified values are the values passed to {@link #publishProgress}.
+     * The default version does nothing.
+     *
+     * @param values The values indicating progress.
+     *
+     * @see #publishProgress
+     * @see #doInBackground
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    @MainThread
+    protected void onProgressUpdate(Progress... values) {
+    }
+
+    /**
+     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+     * {@link #doInBackground(Object[])} has finished.</p>
+     * 
+     * <p>The default implementation simply invokes {@link #onCancelled()} and
+     * ignores the result. If you write your own implementation, do not call
+     * <code>super.onCancelled(result)</code>.</p>
+     *
+     * @param result The result, if any, computed in
+     *               {@link #doInBackground(Object[])}, can be null
+     * 
+     * @see #cancel(boolean)
+     * @see #isCancelled()
+     */
+    @SuppressWarnings({"UnusedParameters"})
+    @MainThread
+    protected void onCancelled(Result result) {
+        onCancelled();
+    }    
+    
+    /**
+     * <p>Applications should preferably override {@link #onCancelled(Object)}.
+     * This method is invoked by the default implementation of
+     * {@link #onCancelled(Object)}.
+     * The default version does nothing.</p>
+     * 
+     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+     * {@link #doInBackground(Object[])} has finished.</p>
+     *
+     * @see #onCancelled(Object) 
+     * @see #cancel(boolean)
+     * @see #isCancelled()
+     */
+    @MainThread
+    protected void onCancelled() {
+    }
+
+    /**
+     * Returns <tt>true</tt> if this task was cancelled before it completed
+     * normally. If you are calling {@link #cancel(boolean)} on the task,
+     * the value returned by this method should be checked periodically from
+     * {@link #doInBackground(Object[])} to end the task as soon as possible.
+     *
+     * @return <tt>true</tt> if task was cancelled before it completed
+     *
+     * @see #cancel(boolean)
+     */
+    public final boolean isCancelled() {
+        return mCancelled.get();
+    }
+
+    /**
+     * <p>Attempts to cancel execution of this task.  This attempt will
+     * fail if the task has already completed, already been cancelled,
+     * or could not be cancelled for some other reason. If successful,
+     * and this task has not started when <tt>cancel</tt> is called,
+     * this task should never run. If the task has already started,
+     * then the <tt>mayInterruptIfRunning</tt> parameter determines
+     * whether the thread executing this task should be interrupted in
+     * an attempt to stop the task.</p>
+     * 
+     * <p>Calling this method will result in {@link #onCancelled(Object)} being
+     * invoked on the UI thread after {@link #doInBackground(Object[])} returns.
+     * Calling this method guarantees that onPostExecute(Object) is never
+     * subsequently invoked, even if <tt>cancel</tt> returns false, but
+     * {@link #onPostExecute} has not yet run.  To finish the
+     * task as early as possible, check {@link #isCancelled()} periodically from
+     * {@link #doInBackground(Object[])}.</p>
+     *
+     * <p>This only requests cancellation. It never waits for a running
+     * background task to terminate, even if <tt>mayInterruptIfRunning</tt> is
+     * true.</p>
+     *
+     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
+     *        task should be interrupted; otherwise, in-progress tasks are allowed
+     *        to complete.
+     *
+     * @return <tt>false</tt> if the task could not be cancelled,
+     *         typically because it has already completed normally;
+     *         <tt>true</tt> otherwise
+     *
+     * @see #isCancelled()
+     * @see #onCancelled(Object)
+     */
+    public final boolean cancel(boolean mayInterruptIfRunning) {
+        mCancelled.set(true);
+        return mFuture.cancel(mayInterruptIfRunning);
+    }
+
+    /**
+     * Waits if necessary for the computation to complete, and then
+     * retrieves its result.
+     *
+     * @return The computed result.
+     *
+     * @throws CancellationException If the computation was cancelled.
+     * @throws ExecutionException If the computation threw an exception.
+     * @throws InterruptedException If the current thread was interrupted
+     *         while waiting.
+     */
+    public final Result get() throws InterruptedException, ExecutionException {
+        return mFuture.get();
+    }
+
+    /**
+     * Waits if necessary for at most the given time for the computation
+     * to complete, and then retrieves its result.
+     *
+     * @param timeout Time to wait before cancelling the operation.
+     * @param unit The time unit for the timeout.
+     *
+     * @return The computed result.
+     *
+     * @throws CancellationException If the computation was cancelled.
+     * @throws ExecutionException If the computation threw an exception.
+     * @throws InterruptedException If the current thread was interrupted
+     *         while waiting.
+     * @throws TimeoutException If the wait timed out.
+     */
+    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
+            ExecutionException, TimeoutException {
+        return mFuture.get(timeout, unit);
+    }
+
+    /**
+     * Executes the task with the specified parameters. The task returns
+     * itself (this) so that the caller can keep a reference to it.
+     * 
+     * <p>Note: this function schedules the task on a queue for a single background
+     * thread or pool of threads depending on the platform version.  When first
+     * introduced, AsyncTasks were executed serially on a single background thread.
+     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
+     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
+     * executed on a single thread to avoid common application errors caused
+     * by parallel execution.  If you truly want parallel execution, you can use
+     * the {@link #executeOnExecutor} version of this method
+     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
+     * on its use.
+     *
+     * <p>This method must be invoked on the UI thread.
+     *
+     * @param params The parameters of the task.
+     *
+     * @return This instance of AsyncTask.
+     *
+     * @throws IllegalStateException If {@link #getStatus()} returns either
+     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+     * @see #execute(Runnable)
+     */
+    @MainThread
+    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
+        return executeOnExecutor(sDefaultExecutor, params);
+    }
+
+    /**
+     * Executes the task with the specified parameters. The task returns
+     * itself (this) so that the caller can keep a reference to it.
+     * 
+     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
+     * allow multiple tasks to run in parallel on a pool of threads managed by
+     * AsyncTask, however you can also use your own {@link Executor} for custom
+     * behavior.
+     * 
+     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
+     * a thread pool is generally <em>not</em> what one wants, because the order
+     * of their operation is not defined.  For example, if these tasks are used
+     * to modify any state in common (such as writing a file due to a button click),
+     * there are no guarantees on the order of the modifications.
+     * Without careful work it is possible in rare cases for the newer version
+     * of the data to be over-written by an older one, leading to obscure data
+     * loss and stability issues.  Such changes are best
+     * executed in serial; to guarantee such work is serialized regardless of
+     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
+     *
+     * <p>This method must be invoked on the UI thread.
+     *
+     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
+     *              convenient process-wide thread pool for tasks that are loosely coupled.
+     * @param params The parameters of the task.
+     *
+     * @return This instance of AsyncTask.
+     *
+     * @throws IllegalStateException If {@link #getStatus()} returns either
+     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+     *
+     * @see #execute(Object[])
+     */
+    @MainThread
+    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
+            Params... params) {
+        if (mStatus != Status.PENDING) {
+            switch (mStatus) {
+                case RUNNING:
+                    throw new IllegalStateException("Cannot execute task:"
+                            + " the task is already running.");
+                case FINISHED:
+                    throw new IllegalStateException("Cannot execute task:"
+                            + " the task has already been executed "
+                            + "(a task can be executed only once)");
+            }
+        }
+
+        mStatus = Status.RUNNING;
+
+        onPreExecute();
+
+        mWorker.mParams = params;
+        exec.execute(mFuture);
+
+        return this;
+    }
+
+    /**
+     * Convenience version of {@link #execute(Object...)} for use with
+     * a simple Runnable object. See {@link #execute(Object[])} for more
+     * information on the order of execution.
+     *
+     * @see #execute(Object[])
+     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+     */
+    @MainThread
+    public static void execute(Runnable runnable) {
+        sDefaultExecutor.execute(runnable);
+    }
+
+    /**
+     * This method can be invoked from {@link #doInBackground} to
+     * publish updates on the UI thread while the background computation is
+     * still running. Each call to this method will trigger the execution of
+     * {@link #onProgressUpdate} on the UI thread.
+     *
+     * {@link #onProgressUpdate} will not be called if the task has been
+     * canceled.
+     *
+     * @param values The progress values to update the UI with.
+     *
+     * @see #onProgressUpdate
+     * @see #doInBackground
+     */
+    @WorkerThread
+    protected final void publishProgress(Progress... values) {
+        if (!isCancelled()) {
+            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
+                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
+        }
+    }
+
+    private void finish(Result result) {
+        if (isCancelled()) {
+            onCancelled(result);
+        } else {
+            onPostExecute(result);
+        }
+        mStatus = Status.FINISHED;
+    }
+
+    private static class InternalHandler extends Handler {
+        public InternalHandler(Looper looper) {
+            super(looper);
+        }
+
+        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
+            switch (msg.what) {
+                case MESSAGE_POST_RESULT:
+                    // There is only one result
+                    result.mTask.finish(result.mData[0]);
+                    break;
+                case MESSAGE_POST_PROGRESS:
+                    result.mTask.onProgressUpdate(result.mData);
+                    break;
+            }
+        }
+    }
+
+    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
+        Params[] mParams;
+    }
+
+    @SuppressWarnings({"RawUseOfParameterizedType"})
+    private static class AsyncTaskResult<Data> {
+        final AsyncTask mTask;
+        final Data[] mData;
+
+        AsyncTaskResult(AsyncTask task, Data... data) {
+            mTask = task;
+            mData = data;
+        }
+    }
+}
diff --git a/android-34/android/os/BadParcelableException.java b/android-34/android/os/BadParcelableException.java
new file mode 100644
index 0000000..9b1343c
--- /dev/null
+++ b/android-34/android/os/BadParcelableException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.util.AndroidRuntimeException;
+
+/**
+ * Exception thrown when a {@link Parcelable} is malformed or otherwise invalid.
+ * <p>
+ * This is typically encountered when a custom {@link Parcelable} object is
+ * passed to another process that doesn't have the same {@link Parcelable} class
+ * in its {@link ClassLoader}.
+ */
+public class BadParcelableException extends AndroidRuntimeException {
+    public BadParcelableException(String msg) {
+        super(msg);
+    }
+    public BadParcelableException(Exception cause) {
+        super(cause);
+    }
+    /** @hide */
+    public BadParcelableException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}
diff --git a/android-34/android/os/BadTypeParcelableException.java b/android-34/android/os/BadTypeParcelableException.java
new file mode 100644
index 0000000..2ca3bd2
--- /dev/null
+++ b/android-34/android/os/BadTypeParcelableException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+/** Used by Parcel to signal that the type on the payload was not expected by the caller. */
+class BadTypeParcelableException extends BadParcelableException {
+    BadTypeParcelableException(String msg) {
+        super(msg);
+    }
+    BadTypeParcelableException(Exception cause) {
+        super(cause);
+    }
+    BadTypeParcelableException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}
diff --git a/android-34/android/os/BaseBundle.java b/android-34/android/os/BaseBundle.java
new file mode 100644
index 0000000..4e3adfb
--- /dev/null
+++ b/android-34/android/os/BaseBundle.java
@@ -0,0 +1,1958 @@
+/*
+ * Copyright (C) 2014 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.os;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+
+import java.io.Serializable;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.function.BiFunction;
+
+/**
+ * A mapping from String keys to values of various types. In most cases, you
+ * should work directly with either the {@link Bundle} or
+ * {@link PersistableBundle} subclass.
+ */
+public class BaseBundle {
+    /** @hide */
+    protected static final String TAG = "Bundle";
+    static final boolean DEBUG = false;
+
+    /**
+     * Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+    private static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N'
+
+    /**
+     * Flag indicating that this Bundle is okay to "defuse", see {@link #setShouldDefuse(boolean)}
+     * for more details.
+     * <p>
+     * This should <em>only</em> be set when the Bundle reaches its final destination, otherwise a
+     * system process may clobber contents that were destined for an app that could have unparceled
+     * them.
+     */
+    static final int FLAG_DEFUSABLE = 1 << 0;
+
+    private static final boolean LOG_DEFUSABLE = false;
+
+    private static volatile boolean sShouldDefuse = false;
+
+    /**
+     * Set global variable indicating that any Bundles parsed in this process should be "defused".
+     * That is, any {@link BadParcelableException} encountered will be suppressed and logged. Also:
+     * <ul>
+     *   <li>If it was the deserialization of a custom item (eg. {@link Parcelable}) that caused the
+     *   exception, {@code null} will be returned but the item will be held in the map in its
+     *   serialized form (lazy value).
+     *   <li>If the exception happened during partial deserialization, that is, during the read of
+     *   the map and its basic types (while skipping custom types), the map will be left empty.
+     * </ul>
+     *
+     * @hide
+     */
+    public static void setShouldDefuse(boolean shouldDefuse) {
+        sShouldDefuse = shouldDefuse;
+    }
+
+    // A parcel cannot be obtained during compile-time initialization. Put the
+    // empty parcel into an inner class that can be initialized separately. This
+    // allows to initialize BaseBundle, and classes depending on it.
+    /** {@hide} */
+    static final class NoImagePreloadHolder {
+        public static final Parcel EMPTY_PARCEL = Parcel.obtain();
+    }
+
+    // Invariant - exactly one of mMap / mParcelledData will be null
+    // (except inside a call to unparcel)
+
+    @UnsupportedAppUsage
+    ArrayMap<String, Object> mMap = null;
+
+    /*
+     * If mParcelledData is non-null, then mMap will be null and the
+     * data are stored as a Parcel containing a Bundle.  When the data
+     * are unparcelled, mParcelledData will be set to null.
+     */
+    @UnsupportedAppUsage
+    volatile Parcel mParcelledData = null;
+
+    /**
+     * Whether {@link #mParcelledData} was generated by native code or not.
+     */
+    private boolean mParcelledByNative;
+
+    /**
+     * Flag indicating if mParcelledData is only referenced in this bundle.
+     * mParcelledData could be referenced elsewhere if mMap contains lazy values,
+     * and bundle data is copied to another bundle using putAll or the copy constructors.
+     */
+    boolean mOwnsLazyValues = true;
+
+    /** Tracks how many lazy values are referenced in mMap */
+    private int mLazyValues = 0;
+
+    /**
+     * As mParcelledData is set to null when it is unparcelled, we keep a weak reference to
+     * it to aid in recycling it. Do not use this reference otherwise.
+     * Is non-null iff mMap contains lazy values.
+    */
+    private WeakReference<Parcel> mWeakParcelledData = null;
+
+    /**
+     * The ClassLoader used when unparcelling data from mParcelledData.
+     */
+    private ClassLoader mClassLoader;
+
+    /** {@hide} */
+    @VisibleForTesting
+    public int mFlags;
+
+    /**
+     * Constructs a new, empty Bundle that uses a specific ClassLoader for
+     * instantiating Parcelable and Serializable objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     * @param capacity Initial size of the ArrayMap.
+     */
+    BaseBundle(@Nullable ClassLoader loader, int capacity) {
+        mMap = capacity > 0 ?
+                new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();
+        mClassLoader = loader == null ? getClass().getClassLoader() : loader;
+    }
+
+    /**
+     * Constructs a new, empty Bundle.
+     */
+    BaseBundle() {
+        this((ClassLoader) null, 0);
+    }
+
+    /**
+     * Constructs a Bundle whose data is stored as a Parcel.  The data
+     * will be unparcelled on first contact, using the assigned ClassLoader.
+     *
+     * @param parcelledData a Parcel containing a Bundle
+     */
+    BaseBundle(Parcel parcelledData) {
+        readFromParcelInner(parcelledData);
+    }
+
+    BaseBundle(Parcel parcelledData, int length) {
+        readFromParcelInner(parcelledData, length);
+    }
+
+    /**
+     * Constructs a new, empty Bundle that uses a specific ClassLoader for
+     * instantiating Parcelable and Serializable objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     */
+    BaseBundle(ClassLoader loader) {
+        this(loader, 0);
+    }
+
+    /**
+     * Constructs a new, empty Bundle sized to hold the given number of
+     * elements. The Bundle will grow as needed.
+     *
+     * @param capacity the initial capacity of the Bundle
+     */
+    BaseBundle(int capacity) {
+        this((ClassLoader) null, capacity);
+    }
+
+    /**
+     * Constructs a Bundle containing a copy of the mappings from the given
+     * Bundle.
+     *
+     * @param b a Bundle to be copied.
+     */
+    BaseBundle(BaseBundle b) {
+        this(b, /* deep */ false);
+    }
+
+    /**
+     * Constructs a {@link BaseBundle} containing a copy of {@code from}.
+     *
+     * @param from The bundle to be copied.
+     * @param deep Whether is a deep or shallow copy.
+     *
+     * @hide
+     */
+    BaseBundle(BaseBundle from, boolean deep) {
+        synchronized (from) {
+            mClassLoader = from.mClassLoader;
+
+            if (from.mMap != null) {
+                mOwnsLazyValues = false;
+                from.mOwnsLazyValues = false;
+
+                if (!deep) {
+                    mMap = new ArrayMap<>(from.mMap);
+                } else {
+                    final ArrayMap<String, Object> fromMap = from.mMap;
+                    final int n = fromMap.size();
+                    mMap = new ArrayMap<>(n);
+                    for (int i = 0; i < n; i++) {
+                        mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
+                    }
+                }
+            } else {
+                mMap = null;
+            }
+
+            final Parcel parcelledData;
+            if (from.mParcelledData != null) {
+                if (from.isEmptyParcel()) {
+                    parcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+                    mParcelledByNative = false;
+                } else {
+                    parcelledData = Parcel.obtain();
+                    parcelledData.appendFrom(from.mParcelledData, 0,
+                            from.mParcelledData.dataSize());
+                    parcelledData.setDataPosition(0);
+                    mParcelledByNative = from.mParcelledByNative;
+                }
+            } else {
+                parcelledData = null;
+                mParcelledByNative = false;
+            }
+
+            // Keep as last statement to ensure visibility of other fields
+            mParcelledData = parcelledData;
+        }
+    }
+
+    /**
+     * TODO: optimize this later (getting just the value part of a Bundle
+     * with a single pair) once Bundle.forPair() above is implemented
+     * with a special single-value Map implementation/serialization.
+     *
+     * Note: value in single-pair Bundle may be null.
+     *
+     * @hide
+     */
+    public String getPairValue() {
+        unparcel();
+        int size = mMap.size();
+        if (size > 1) {
+            Log.w(TAG, "getPairValue() used on Bundle with multiple pairs.");
+        }
+        if (size == 0) {
+            return null;
+        }
+        try {
+            return getValueAt(0, String.class);
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning("getPairValue()", "String", e);
+            return null;
+        }
+    }
+
+    /**
+     * Changes the ClassLoader this Bundle uses when instantiating objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     */
+    void setClassLoader(ClassLoader loader) {
+        mClassLoader = loader;
+    }
+
+    /**
+     * Return the ClassLoader currently associated with this Bundle.
+     */
+    ClassLoader getClassLoader() {
+        return mClassLoader;
+    }
+
+    /**
+     * If the underlying data are stored as a Parcel, unparcel them
+     * using the currently assigned class loader.
+     */
+    @UnsupportedAppUsage
+    final void unparcel() {
+        unparcel(/* itemwise */ false);
+    }
+
+    /** Deserializes the underlying data and each item if {@code itemwise} is true. */
+    final void unparcel(boolean itemwise) {
+        synchronized (this) {
+            final Parcel source = mParcelledData;
+            if (source != null) {
+                Preconditions.checkState(mOwnsLazyValues);
+                initializeFromParcelLocked(source, /*ownsParcel*/ true, mParcelledByNative);
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "unparcel "
+                            + Integer.toHexString(System.identityHashCode(this))
+                            + ": no parcelled data");
+                }
+            }
+            if (itemwise) {
+                if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
+                    Slog.wtf(TAG,
+                            "Attempting to unparcel all items in a Bundle while in transit; this "
+                                    + "may remove elements intended for the final desitination.",
+                            new Throwable());
+                }
+                for (int i = 0, n = mMap.size(); i < n; i++) {
+                    // Triggers deserialization of i-th item, if needed
+                    getValueAt(i, /* clazz */ null);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the value for key {@code key}.
+     *
+     * This call should always be made after {@link #unparcel()} or inside a lock after making sure
+     * {@code mMap} is not null.
+     *
+     * @deprecated Use {@link #getValue(String, Class, Class[])}. This method should only be used in
+     *      other deprecated APIs.
+     *
+     * @hide
+     */
+    @Deprecated
+    @Nullable
+    final Object getValue(String key) {
+        return getValue(key, /* clazz */ null);
+    }
+
+    /** Same as {@link #getValue(String, Class, Class[])} with no item types. */
+    @Nullable
+    final <T> T getValue(String key, @Nullable Class<T> clazz) {
+        // Avoids allocating Class[0] array
+        return getValue(key, clazz, (Class<?>[]) null);
+    }
+
+    /**
+     * Returns the value for key {@code key} for expected return type {@code clazz} (or pass {@code
+     * null} for no type check).
+     *
+     * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
+     *
+     * This call should always be made after {@link #unparcel()} or inside a lock after making sure
+     * {@code mMap} is not null.
+     *
+     * @hide
+     */
+    @Nullable
+    final <T> T getValue(String key, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
+        int i = mMap.indexOfKey(key);
+        return (i >= 0) ? getValueAt(i, clazz, itemTypes) : null;
+    }
+
+    /**
+     * Returns the value for a certain position in the array map for expected return type {@code
+     * clazz} (or pass {@code null} for no type check).
+     *
+     * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
+     *
+     * This call should always be made after {@link #unparcel()} or inside a lock after making sure
+     * {@code mMap} is not null.
+     *
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    final <T> T getValueAt(int i, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
+        Object object = mMap.valueAt(i);
+        if (object instanceof BiFunction<?, ?, ?>) {
+            synchronized (this) {
+                object = unwrapLazyValueFromMapLocked(i, clazz, itemTypes);
+            }
+        }
+        return (clazz != null) ? clazz.cast(object) : (T) object;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    @GuardedBy("this")
+    private Object unwrapLazyValueFromMapLocked(int i, @Nullable Class<?> clazz,
+            @Nullable Class<?>... itemTypes) {
+        Object object = mMap.valueAt(i);
+        if (object instanceof BiFunction<?, ?, ?>) {
+            try {
+                object = ((BiFunction<Class<?>, Class<?>[], ?>) object).apply(clazz, itemTypes);
+            } catch (BadParcelableException e) {
+                if (sShouldDefuse) {
+                    Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
+                    return null;
+                } else {
+                    throw e;
+                }
+            }
+            mMap.setValueAt(i, object);
+            mLazyValues--;
+            if (mOwnsLazyValues) {
+                Preconditions.checkState(mLazyValues >= 0,
+                        "Lazy values ref count below 0");
+                // No more lazy values in mMap, so we can recycle the parcel early rather than
+                // waiting for the next GC run
+                if (mLazyValues == 0) {
+                    Preconditions.checkState(mWeakParcelledData.get() != null,
+                            "Parcel recycled earlier than expected");
+                    recycleParcel(mWeakParcelledData.get());
+                    mWeakParcelledData = null;
+                }
+            }
+        }
+        return object;
+    }
+
+    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean ownsParcel,
+            boolean parcelledByNative) {
+        if (isEmptyParcel(parcelledData)) {
+            if (DEBUG) {
+                Log.d(TAG, "unparcel "
+                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
+            }
+            if (mMap == null) {
+                mMap = new ArrayMap<>(1);
+            } else {
+                mMap.erase();
+            }
+            mParcelledByNative = false;
+            mParcelledData = null;
+            return;
+        }
+
+        final int count = parcelledData.readInt();
+        if (DEBUG) {
+            Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                    + ": reading " + count + " maps");
+        }
+        if (count < 0) {
+            return;
+        }
+        ArrayMap<String, Object> map = mMap;
+        if (map == null) {
+            map = new ArrayMap<>(count);
+        } else {
+            map.erase();
+            map.ensureCapacity(count);
+        }
+        int numLazyValues = 0;
+        try {
+            numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
+                    /* lazy */ ownsParcel, mClassLoader);
+        } catch (BadParcelableException e) {
+            if (sShouldDefuse) {
+                Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
+                map.erase();
+            } else {
+                throw e;
+            }
+        } finally {
+            mWeakParcelledData = null;
+            if (ownsParcel) {
+                if (numLazyValues == 0) {
+                    recycleParcel(parcelledData);
+                } else {
+                    mWeakParcelledData = new WeakReference<>(parcelledData);
+                }
+            }
+
+            mLazyValues = numLazyValues;
+            mParcelledByNative = false;
+            mMap = map;
+            // Set field last as it is volatile
+            mParcelledData = null;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                    + " final map: " + mMap);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean isParcelled() {
+        return mParcelledData != null;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isEmptyParcel() {
+        return isEmptyParcel(mParcelledData);
+    }
+
+    /**
+     * @hide
+     */
+    private static boolean isEmptyParcel(Parcel p) {
+        return p == NoImagePreloadHolder.EMPTY_PARCEL;
+    }
+
+    private static void recycleParcel(Parcel p) {
+        if (p != null && !isEmptyParcel(p)) {
+            p.recycle();
+        }
+    }
+
+    /**
+     * Returns the backing map of this bundle after deserializing every item.
+     *
+     * <p><b>Warning:</b> This method will deserialize every item on the bundle, including custom
+     * types such as {@link Parcelable} and {@link Serializable}, so only use this when you trust
+     * the source. Specifically don't use this method on app-provided bundles.
+     *
+     * @hide
+     */
+    ArrayMap<String, Object> getItemwiseMap() {
+        unparcel(/* itemwise */ true);
+        return mMap;
+    }
+
+    /**
+     * Returns the number of mappings contained in this Bundle.
+     *
+     * @return the number of mappings as an int.
+     */
+    public int size() {
+        unparcel();
+        return mMap.size();
+    }
+
+    /**
+     * Returns true if the mapping of this Bundle is empty, false otherwise.
+     */
+    public boolean isEmpty() {
+        unparcel();
+        return mMap.isEmpty();
+    }
+
+    /**
+     * This method returns true when the parcel is 'definitely' empty.
+     * That is, it may return false for an empty parcel. But will never return true for a non-empty
+     * one.
+     *
+     * @hide this should probably be the implementation of isEmpty().  To do that we
+     * need to ensure we always use the special empty parcel form when the bundle is
+     * empty.  (This may already be the case, but to be safe we'll do this later when
+     * we aren't trying to stabilize.)
+     */
+    public boolean isDefinitelyEmpty() {
+        if (isParcelled()) {
+            return isEmptyParcel();
+        } else {
+            return isEmpty();
+        }
+    }
+
+    /**
+     * Does a loose equality check between two given {@link BaseBundle} objects.
+     * Returns {@code true} if both are {@code null}, or if both are equal as per
+     * {@link #kindofEquals(BaseBundle)}
+     *
+     * @param a A {@link BaseBundle} object
+     * @param b Another {@link BaseBundle} to compare with a
+     * @return {@code true} if both are the same, {@code false} otherwise
+     *
+     * @see #kindofEquals(BaseBundle)
+     *
+     * @hide
+     */
+    public static boolean kindofEquals(@Nullable BaseBundle a, @Nullable BaseBundle b) {
+        return (a == b) || (a != null && a.kindofEquals(b));
+    }
+
+    /**
+     * Performs a loose equality check, which means there can be false negatives but if the method
+     * returns true than both objects are guaranteed to be equal.
+     *
+     * The point is that this method is a light-weight check in performance terms.
+     *
+     * @hide
+     */
+    public boolean kindofEquals(BaseBundle other) {
+        if (other == null) {
+            return false;
+        }
+        if (isDefinitelyEmpty() && other.isDefinitelyEmpty()) {
+            return true;
+        }
+        if (isParcelled() != other.isParcelled()) {
+            // Big kind-of here!
+            return false;
+        } else if (isParcelled()) {
+            return mParcelledData.compareData(other.mParcelledData) == 0;
+        } else {
+            // Following semantic above of failing in case we get a serialized value vs a
+            // deserialized one, we'll compare the map. If a certain element hasn't been
+            // deserialized yet, it's a function object (or more specifically a LazyValue, but let's
+            // pretend we don't know that here :P), we'll use that element's equality comparison as
+            // map naturally does. That will takes care of comparing the payload if needed (see
+            // Parcel.readLazyValue() for details).
+            return mMap.equals(other.mMap);
+        }
+    }
+
+    /**
+     * Removes all elements from the mapping of this Bundle.
+     * Recycles the underlying parcel if it is still present.
+     */
+    public void clear() {
+        unparcel();
+        if (mOwnsLazyValues && mWeakParcelledData != null) {
+            recycleParcel(mWeakParcelledData.get());
+        }
+
+        mWeakParcelledData = null;
+        mLazyValues = 0;
+        mOwnsLazyValues = true;
+        mMap.clear();
+    }
+
+    private Object deepCopyValue(Object value) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof Bundle) {
+            return ((Bundle)value).deepCopy();
+        } else if (value instanceof PersistableBundle) {
+            return ((PersistableBundle)value).deepCopy();
+        } else if (value instanceof ArrayList) {
+            return deepcopyArrayList((ArrayList) value);
+        } else if (value.getClass().isArray()) {
+            if (value instanceof int[]) {
+                return ((int[])value).clone();
+            } else if (value instanceof long[]) {
+                return ((long[])value).clone();
+            } else if (value instanceof float[]) {
+                return ((float[])value).clone();
+            } else if (value instanceof double[]) {
+                return ((double[])value).clone();
+            } else if (value instanceof Object[]) {
+                return ((Object[])value).clone();
+            } else if (value instanceof byte[]) {
+                return ((byte[])value).clone();
+            } else if (value instanceof short[]) {
+                return ((short[])value).clone();
+            } else if (value instanceof char[]) {
+                return ((char[]) value).clone();
+            }
+        }
+        return value;
+    }
+
+    private ArrayList deepcopyArrayList(ArrayList from) {
+        final int N = from.size();
+        ArrayList out = new ArrayList(N);
+        for (int i=0; i<N; i++) {
+            out.add(deepCopyValue(from.get(i)));
+        }
+        return out;
+    }
+
+    /**
+     * Returns true if the given key is contained in the mapping
+     * of this Bundle.
+     *
+     * @param key a String key
+     * @return true if the key is part of the mapping, false otherwise
+     */
+    public boolean containsKey(String key) {
+        unparcel();
+        return mMap.containsKey(key);
+    }
+
+    /**
+     * Returns the entry with the given key as an object.
+     *
+     * @param key a String key
+     * @return an Object, or null
+     *
+     * @deprecated Use the type-safe specific APIs depending on the type of the item to be
+     *      retrieved, eg. {@link #getString(String)}.
+     */
+    @Deprecated
+    @Nullable
+    public Object get(String key) {
+        unparcel();
+        return getValue(key);
+    }
+
+    /**
+     * Returns the object of type {@code clazz} for the given {@code key}, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p>Use the more specific APIs where possible, especially in the case of containers such as
+     * lists, since those APIs allow you to specify the type of the items.
+     *
+     * @param key String key
+     * @param clazz The type of the object expected
+     * @return an Object, or null
+     */
+    @Nullable
+    <T> T get(@Nullable String key, @NonNull Class<T> clazz) {
+        unparcel();
+        try {
+            return getValue(key, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, clazz.getCanonicalName(), e);
+            return null;
+        }
+    }
+
+    /**
+     * Removes any entry with the given key from the mapping of this Bundle.
+     *
+     * @param key a String key
+     */
+    public void remove(String key) {
+        unparcel();
+        mMap.remove(key);
+    }
+
+    /**
+     * Inserts all mappings from the given PersistableBundle into this BaseBundle.
+     *
+     * @param bundle a PersistableBundle
+     */
+    public void putAll(PersistableBundle bundle) {
+        unparcel();
+        bundle.unparcel();
+        mMap.putAll(bundle.mMap);
+    }
+
+    /**
+     * Inserts all mappings from the given Map into this BaseBundle.
+     *
+     * @param map a Map
+     */
+    void putAll(ArrayMap map) {
+        unparcel();
+        mMap.putAll(map);
+    }
+
+    /**
+     * Returns a Set containing the Strings used as keys in this Bundle.
+     *
+     * @return a Set of String keys
+     */
+    public Set<String> keySet() {
+        unparcel();
+        return mMap.keySet();
+    }
+
+    /** {@hide} */
+    public void putObject(@Nullable String key, @Nullable Object value) {
+        if (value == null) {
+            putString(key, null);
+        } else if (value instanceof Boolean) {
+            putBoolean(key, (Boolean) value);
+        } else if (value instanceof Integer) {
+            putInt(key, (Integer) value);
+        } else if (value instanceof Long) {
+            putLong(key, (Long) value);
+        } else if (value instanceof Double) {
+            putDouble(key, (Double) value);
+        } else if (value instanceof String) {
+            putString(key, (String) value);
+        } else if (value instanceof boolean[]) {
+            putBooleanArray(key, (boolean[]) value);
+        } else if (value instanceof int[]) {
+            putIntArray(key, (int[]) value);
+        } else if (value instanceof long[]) {
+            putLongArray(key, (long[]) value);
+        } else if (value instanceof double[]) {
+            putDoubleArray(key, (double[]) value);
+        } else if (value instanceof String[]) {
+            putStringArray(key, (String[]) value);
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + value.getClass());
+        }
+    }
+
+    /**
+     * Inserts a Boolean value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a boolean
+     */
+    public void putBoolean(@Nullable String key, boolean value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a byte value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a byte
+     */
+    void putByte(@Nullable String key, byte value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a char value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a char
+     */
+    void putChar(@Nullable String key, char value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a short value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a short
+     */
+    void putShort(@Nullable String key, short value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an int value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value an int
+     */
+    public void putInt(@Nullable String key, int value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a long value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a long
+     */
+    public void putLong(@Nullable String key, long value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a float value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a float
+     */
+    void putFloat(@Nullable String key, float value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a double value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a double
+     */
+    public void putDouble(@Nullable String key, double value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a String value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a String, or null
+     */
+    public void putString(@Nullable String key, @Nullable String value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a CharSequence value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a CharSequence, or null
+     */
+    void putCharSequence(@Nullable String key, @Nullable CharSequence value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<Integer> object, or null
+     */
+    void putIntegerArrayList(@Nullable String key, @Nullable ArrayList<Integer> value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<String> object, or null
+     */
+    void putStringArrayList(@Nullable String key, @Nullable ArrayList<String> value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<CharSequence> object, or null
+     */
+    void putCharSequenceArrayList(@Nullable String key, @Nullable ArrayList<CharSequence> value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a Serializable value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Serializable object, or null
+     */
+    void putSerializable(@Nullable String key, @Nullable Serializable value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a boolean array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a boolean array object, or null
+     */
+    public void putBooleanArray(@Nullable String key, @Nullable boolean[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a byte array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a byte array object, or null
+     */
+    void putByteArray(@Nullable String key, @Nullable byte[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a short array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a short array object, or null
+     */
+    void putShortArray(@Nullable String key, @Nullable short[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a char array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a char array object, or null
+     */
+    void putCharArray(@Nullable String key, @Nullable char[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an int array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an int array object, or null
+     */
+    public void putIntArray(@Nullable String key, @Nullable int[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a long array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a long array object, or null
+     */
+    public void putLongArray(@Nullable String key, @Nullable long[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a float array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a float array object, or null
+     */
+    void putFloatArray(@Nullable String key, @Nullable float[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a double array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a double array object, or null
+     */
+    public void putDoubleArray(@Nullable String key, @Nullable double[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a String array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a String array object, or null
+     */
+    public void putStringArray(@Nullable String key, @Nullable String[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a CharSequence array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a CharSequence array object, or null
+     */
+    void putCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Returns the value associated with the given key, or false if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a boolean value
+     */
+    public boolean getBoolean(String key) {
+        unparcel();
+        if (DEBUG) Log.d(TAG, "Getting boolean in "
+                + Integer.toHexString(System.identityHashCode(this)));
+        return getBoolean(key, false);
+    }
+
+    // Log a message if the value was non-null but not of the expected type
+    void typeWarning(String key, @Nullable Object value, String className,
+            Object defaultValue, RuntimeException e) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Key ");
+        sb.append(key);
+        sb.append(" expected ");
+        sb.append(className);
+        if (value != null) {
+            sb.append(" but value was a ");
+            sb.append(value.getClass().getName());
+        } else {
+            sb.append(" but value was of a different type");
+        }
+        sb.append(".  The default value ");
+        sb.append(defaultValue);
+        sb.append(" was returned.");
+        Log.w(TAG, sb.toString());
+        Log.w(TAG, "Attempt to cast generated internal exception:", e);
+    }
+
+    void typeWarning(String key, @Nullable Object value, String className, RuntimeException e) {
+        typeWarning(key, value, className, "<null>", e);
+    }
+
+    void typeWarning(String key, String className, RuntimeException e) {
+        typeWarning(key, /* value */ null, className, "<null>", e);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a boolean value
+     */
+    public boolean getBoolean(String key, boolean defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Boolean) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Boolean", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or (byte) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a byte value
+     */
+    byte getByte(String key) {
+        unparcel();
+        return getByte(key, (byte) 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a byte value
+     */
+    Byte getByte(String key, byte defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Byte) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Byte", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or (char) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a char value
+     */
+    char getChar(String key) {
+        unparcel();
+        return getChar(key, (char) 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a char value
+     */
+    char getChar(String key, char defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Character) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Character", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or (short) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a short value
+     */
+    short getShort(String key) {
+        unparcel();
+        return getShort(key, (short) 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a short value
+     */
+    short getShort(String key, short defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Short) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Short", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return an int value
+     */
+    public int getInt(String key) {
+        unparcel();
+        return getInt(key, 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return an int value
+     */
+   public int getInt(String key, int defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Integer) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Integer", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0L if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a long value
+     */
+    public long getLong(String key) {
+        unparcel();
+        return getLong(key, 0L);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a long value
+     */
+    public long getLong(String key, long defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Long) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Long", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0.0f if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a float value
+     */
+    float getFloat(String key) {
+        unparcel();
+        return getFloat(key, 0.0f);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a float value
+     */
+    float getFloat(String key, float defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Float) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Float", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0.0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a double value
+     */
+    public double getDouble(String key) {
+        unparcel();
+        return getDouble(key, 0.0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a double value
+     */
+    public double getDouble(String key, double defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Double) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Double", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a String value, or null
+     */
+    @Nullable
+    public String getString(@Nullable String key) {
+        unparcel();
+        final Object o = mMap.get(key);
+        try {
+            return (String) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "String", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key or if a null
+     * value is explicitly associated with the given key.
+     *
+     * @param key a String, or null
+     * @param defaultValue Value to return if key does not exist or if a null
+     *     value is associated with the given key.
+     * @return the String value associated with the given key, or defaultValue
+     *     if no valid String object is currently mapped to that key.
+     */
+    public String getString(@Nullable String key, String defaultValue) {
+        final String s = getString(key);
+        return (s == null) ? defaultValue : s;
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a CharSequence value, or null
+     */
+    @Nullable
+    CharSequence getCharSequence(@Nullable String key) {
+        unparcel();
+        final Object o = mMap.get(key);
+        try {
+            return (CharSequence) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "CharSequence", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key or if a null
+     * value is explicitly associated with the given key.
+     *
+     * @param key a String, or null
+     * @param defaultValue Value to return if key does not exist or if a null
+     *     value is associated with the given key.
+     * @return the CharSequence value associated with the given key, or defaultValue
+     *     if no valid CharSequence object is currently mapped to that key.
+     */
+    CharSequence getCharSequence(@Nullable String key, CharSequence defaultValue) {
+        final CharSequence cs = getCharSequence(key);
+        return (cs == null) ? defaultValue : cs;
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Serializable value, or null
+     *
+     * @deprecated Use {@link #getSerializable(String, Class)}. This method should only be used in
+     *      other deprecated APIs.
+     */
+    @Deprecated
+    @Nullable
+    Serializable getSerializable(@Nullable String key) {
+        unparcel();
+        Object o = getValue(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (Serializable) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Serializable", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * @param key a String, or null
+     * @param clazz The expected class of the returned type
+     * @return a Serializable value, or null
+     */
+    @Nullable
+    <T extends Serializable> T getSerializable(@Nullable String key, @NonNull Class<T> clazz) {
+        return get(key, clazz);
+    }
+
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    <T> ArrayList<T> getArrayList(@Nullable String key, @NonNull Class<? extends T> clazz) {
+        unparcel();
+        try {
+            return getValue(key, ArrayList.class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, "ArrayList<" + clazz.getCanonicalName() + ">", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<String> value, or null
+     */
+    @Nullable
+    ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
+        return getArrayList(key, Integer.class);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<String> value, or null
+     */
+    @Nullable
+    ArrayList<String> getStringArrayList(@Nullable String key) {
+        return getArrayList(key, String.class);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<CharSequence> value, or null
+     */
+    @Nullable
+    ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
+        return getArrayList(key, CharSequence.class);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a boolean[] value, or null
+     */
+    @Nullable
+    public boolean[] getBooleanArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (boolean[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "byte[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a byte[] value, or null
+     */
+    @Nullable
+    byte[] getByteArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (byte[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "byte[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a short[] value, or null
+     */
+    @Nullable
+    short[] getShortArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (short[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "short[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a char[] value, or null
+     */
+    @Nullable
+    char[] getCharArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (char[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "char[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an int[] value, or null
+     */
+    @Nullable
+    public int[] getIntArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (int[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "int[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a long[] value, or null
+     */
+    @Nullable
+    public long[] getLongArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (long[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "long[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a float[] value, or null
+     */
+    @Nullable
+    float[] getFloatArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (float[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "float[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a double[] value, or null
+     */
+    @Nullable
+    public double[] getDoubleArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (double[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "double[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a String[] value, or null
+     */
+    @Nullable
+    public String[] getStringArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (String[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "String[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a CharSequence[] value, or null
+     */
+    @Nullable
+    CharSequence[] getCharSequenceArray(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (CharSequence[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "CharSequence[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Writes the Bundle contents to a Parcel, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to copy this bundle to.
+     */
+    void writeToParcelInner(Parcel parcel, int flags) {
+        // If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
+        if (parcel.hasReadWriteHelper()) {
+            unparcel(/* itemwise */ true);
+        }
+        // Keep implementation in sync with writeToParcel() in
+        // frameworks/native/libs/binder/PersistableBundle.cpp.
+        final ArrayMap<String, Object> map;
+        synchronized (this) {
+            // unparcel() can race with this method and cause the parcel to recycle
+            // at the wrong time. So synchronize access the mParcelledData's content.
+            if (mParcelledData != null) {
+                if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
+                    parcel.writeInt(0);
+                } else {
+                    int length = mParcelledData.dataSize();
+                    parcel.writeInt(length);
+                    parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
+                    parcel.appendFrom(mParcelledData, 0, length);
+                }
+                return;
+            }
+            map = mMap;
+        }
+
+        // Special case for empty bundles.
+        if (map == null || map.size() <= 0) {
+            parcel.writeInt(0);
+            return;
+        }
+        int lengthPos = parcel.dataPosition();
+        parcel.writeInt(-1); // placeholder, will hold length
+        parcel.writeInt(BUNDLE_MAGIC);
+
+        int startPos = parcel.dataPosition();
+        parcel.writeArrayMapInternal(map);
+        int endPos = parcel.dataPosition();
+
+        // Backpatch length
+        parcel.setDataPosition(lengthPos);
+        int length = endPos - startPos;
+        parcel.writeInt(length);
+        parcel.setDataPosition(endPos);
+    }
+
+    /**
+     * Reads the Parcel contents into this Bundle, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to overwrite this bundle from.
+     */
+    void readFromParcelInner(Parcel parcel) {
+        // Keep implementation in sync with readFromParcel() in
+        // frameworks/native/libs/binder/PersistableBundle.cpp.
+        int length = parcel.readInt();
+        readFromParcelInner(parcel, length);
+    }
+
+    private void readFromParcelInner(Parcel parcel, int length) {
+        if (length < 0) {
+            throw new RuntimeException("Bad length in parcel: " + length);
+        } else if (length == 0) {
+            mParcelledByNative = false;
+            // Empty Bundle or end of data.
+            mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+            return;
+        } else if (length % 4 != 0) {
+            throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
+        }
+
+        final int magic = parcel.readInt();
+        final boolean isJavaBundle = magic == BUNDLE_MAGIC;
+        final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
+        if (!isJavaBundle && !isNativeBundle) {
+            throw new IllegalStateException("Bad magic number for Bundle: 0x"
+                    + Integer.toHexString(magic));
+        }
+
+        if (parcel.hasReadWriteHelper()) {
+            // If the parcel has a read-write helper, it's better to deserialize immediately
+            // otherwise the helper would have to either maintain valid state long after the bundle
+            // had been constructed with parcel or to make sure they trigger deserialization of the
+            // bundle immediately; neither of which is obvious.
+            synchronized (this) {
+                mOwnsLazyValues = false;
+                initializeFromParcelLocked(parcel, /*ownsParcel*/ false, isNativeBundle);
+            }
+            return;
+        }
+
+        // Advance within this Parcel
+        int offset = parcel.dataPosition();
+        parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
+
+        Parcel p = Parcel.obtain();
+        p.setDataPosition(0);
+        p.appendFrom(parcel, offset, length);
+        p.adoptClassCookies(parcel);
+        if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
+                + ": " + length + " bundle bytes starting at " + offset);
+        p.setDataPosition(0);
+
+        mOwnsLazyValues = true;
+        mParcelledByNative = isNativeBundle;
+        mParcelledData = p;
+    }
+
+    /** {@hide} */
+    public static void dumpStats(IndentingPrintWriter pw, String key, Object value) {
+        final Parcel tmp = Parcel.obtain();
+        tmp.writeValue(value);
+        final int size = tmp.dataPosition();
+        tmp.recycle();
+
+        // We only really care about logging large values
+        if (size > 1024) {
+            pw.println(key + " [size=" + size + "]");
+            if (value instanceof BaseBundle) {
+                dumpStats(pw, (BaseBundle) value);
+            } else if (value instanceof SparseArray) {
+                dumpStats(pw, (SparseArray) value);
+            }
+        }
+    }
+
+    /** {@hide} */
+    public static void dumpStats(IndentingPrintWriter pw, SparseArray array) {
+        pw.increaseIndent();
+        if (array == null) {
+            pw.println("[null]");
+            return;
+        }
+        for (int i = 0; i < array.size(); i++) {
+            dumpStats(pw, "0x" + Integer.toHexString(array.keyAt(i)), array.valueAt(i));
+        }
+        pw.decreaseIndent();
+    }
+
+    /** {@hide} */
+    public static void dumpStats(IndentingPrintWriter pw, BaseBundle bundle) {
+        pw.increaseIndent();
+        if (bundle == null) {
+            pw.println("[null]");
+            return;
+        }
+        final ArrayMap<String, Object> map = bundle.getItemwiseMap();
+        for (int i = 0; i < map.size(); i++) {
+            dumpStats(pw, map.keyAt(i), map.valueAt(i));
+        }
+        pw.decreaseIndent();
+    }
+}
diff --git a/android-34/android/os/BatteryConsumer.java b/android-34/android/os/BatteryConsumer.java
new file mode 100644
index 0000000..0ba8d51
--- /dev/null
+++ b/android-34/android/os/BatteryConsumer.java
@@ -0,0 +1,911 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.database.CursorWindow;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * Interface for objects containing battery attribution data.
+ *
+ * @hide
+ */
+public abstract class BatteryConsumer {
+
+    private static final String TAG = "BatteryConsumer";
+
+    /**
+     * Power usage component, describing the particular part of the system
+     * responsible for power drain.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"POWER_COMPONENT_"}, value = {
+            POWER_COMPONENT_ANY,
+            POWER_COMPONENT_SCREEN,
+            POWER_COMPONENT_CPU,
+            POWER_COMPONENT_BLUETOOTH,
+            POWER_COMPONENT_CAMERA,
+            POWER_COMPONENT_AUDIO,
+            POWER_COMPONENT_VIDEO,
+            POWER_COMPONENT_FLASHLIGHT,
+            POWER_COMPONENT_MOBILE_RADIO,
+            POWER_COMPONENT_SYSTEM_SERVICES,
+            POWER_COMPONENT_SENSORS,
+            POWER_COMPONENT_GNSS,
+            POWER_COMPONENT_WIFI,
+            POWER_COMPONENT_WAKELOCK,
+            POWER_COMPONENT_MEMORY,
+            POWER_COMPONENT_PHONE,
+            POWER_COMPONENT_IDLE,
+            POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public static @interface PowerComponent {
+    }
+
+    public static final int POWER_COMPONENT_ANY = -1;
+    public static final int POWER_COMPONENT_SCREEN = OsProtoEnums.POWER_COMPONENT_SCREEN; // 0
+    public static final int POWER_COMPONENT_CPU = OsProtoEnums.POWER_COMPONENT_CPU; // 1
+    public static final int POWER_COMPONENT_BLUETOOTH = OsProtoEnums.POWER_COMPONENT_BLUETOOTH; // 2
+    public static final int POWER_COMPONENT_CAMERA = OsProtoEnums.POWER_COMPONENT_CAMERA; // 3
+    public static final int POWER_COMPONENT_AUDIO = OsProtoEnums.POWER_COMPONENT_AUDIO; // 4
+    public static final int POWER_COMPONENT_VIDEO = OsProtoEnums.POWER_COMPONENT_VIDEO; // 5
+    public static final int POWER_COMPONENT_FLASHLIGHT =
+            OsProtoEnums.POWER_COMPONENT_FLASHLIGHT; // 6
+    public static final int POWER_COMPONENT_SYSTEM_SERVICES =
+            OsProtoEnums.POWER_COMPONENT_SYSTEM_SERVICES; // 7
+    public static final int POWER_COMPONENT_MOBILE_RADIO =
+            OsProtoEnums.POWER_COMPONENT_MOBILE_RADIO; // 8
+    public static final int POWER_COMPONENT_SENSORS = OsProtoEnums.POWER_COMPONENT_SENSORS; // 9
+    public static final int POWER_COMPONENT_GNSS = OsProtoEnums.POWER_COMPONENT_GNSS; // 10
+    public static final int POWER_COMPONENT_WIFI = OsProtoEnums.POWER_COMPONENT_WIFI; // 11
+    public static final int POWER_COMPONENT_WAKELOCK = OsProtoEnums.POWER_COMPONENT_WAKELOCK; // 12
+    public static final int POWER_COMPONENT_MEMORY = OsProtoEnums.POWER_COMPONENT_MEMORY; // 13
+    public static final int POWER_COMPONENT_PHONE = OsProtoEnums.POWER_COMPONENT_PHONE; // 14
+    public static final int POWER_COMPONENT_AMBIENT_DISPLAY =
+            OsProtoEnums.POWER_COMPONENT_AMBIENT_DISPLAY; // 15
+    public static final int POWER_COMPONENT_IDLE = OsProtoEnums.POWER_COMPONENT_IDLE; // 16
+    // Power that is re-attributed to other battery consumers. For example, for System Server
+    // this represents the power attributed to apps requesting system services.
+    // The value should be negative or zero.
+    public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS =
+            OsProtoEnums.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS; // 17
+
+    public static final int POWER_COMPONENT_COUNT = 18;
+
+    public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
+    public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
+
+    private static final String[] sPowerComponentNames = new String[POWER_COMPONENT_COUNT];
+
+    static {
+        // Assign individually to avoid future mismatch
+        sPowerComponentNames[POWER_COMPONENT_SCREEN] = "screen";
+        sPowerComponentNames[POWER_COMPONENT_CPU] = "cpu";
+        sPowerComponentNames[POWER_COMPONENT_BLUETOOTH] = "bluetooth";
+        sPowerComponentNames[POWER_COMPONENT_CAMERA] = "camera";
+        sPowerComponentNames[POWER_COMPONENT_AUDIO] = "audio";
+        sPowerComponentNames[POWER_COMPONENT_VIDEO] = "video";
+        sPowerComponentNames[POWER_COMPONENT_FLASHLIGHT] = "flashlight";
+        sPowerComponentNames[POWER_COMPONENT_SYSTEM_SERVICES] = "system_services";
+        sPowerComponentNames[POWER_COMPONENT_MOBILE_RADIO] = "mobile_radio";
+        sPowerComponentNames[POWER_COMPONENT_SENSORS] = "sensors";
+        sPowerComponentNames[POWER_COMPONENT_GNSS] = "gnss";
+        sPowerComponentNames[POWER_COMPONENT_WIFI] = "wifi";
+        sPowerComponentNames[POWER_COMPONENT_WAKELOCK] = "wakelock";
+        sPowerComponentNames[POWER_COMPONENT_MEMORY] = "memory";
+        sPowerComponentNames[POWER_COMPONENT_PHONE] = "phone";
+        sPowerComponentNames[POWER_COMPONENT_AMBIENT_DISPLAY] = "ambient_display";
+        sPowerComponentNames[POWER_COMPONENT_IDLE] = "idle";
+        sPowerComponentNames[POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS] = "reattributed";
+    }
+
+    /**
+     * Identifiers of models used for power estimation.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"POWER_MODEL_"}, value = {
+            POWER_MODEL_UNDEFINED,
+            POWER_MODEL_POWER_PROFILE,
+            POWER_MODEL_ENERGY_CONSUMPTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PowerModel {
+    }
+
+    /**
+     * Unspecified power model.
+     */
+    public static final int POWER_MODEL_UNDEFINED = 0;
+
+    /**
+     * Power model that is based on average consumption rates that hardware components
+     * consume in various states.
+     */
+    public static final int POWER_MODEL_POWER_PROFILE = 1;
+
+    /**
+     * Power model that is based on energy consumption stats provided by PowerStats HAL.
+     */
+    public static final int POWER_MODEL_ENERGY_CONSUMPTION = 2;
+
+    /**
+     * Identifiers of consumed power aggregations.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"PROCESS_STATE_"}, value = {
+            PROCESS_STATE_ANY,
+            PROCESS_STATE_UNSPECIFIED,
+            PROCESS_STATE_FOREGROUND,
+            PROCESS_STATE_BACKGROUND,
+            PROCESS_STATE_FOREGROUND_SERVICE,
+            PROCESS_STATE_CACHED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProcessState {
+    }
+
+    public static final int PROCESS_STATE_UNSPECIFIED = 0;
+    public static final int PROCESS_STATE_ANY = PROCESS_STATE_UNSPECIFIED;
+    public static final int PROCESS_STATE_FOREGROUND = 1;
+    public static final int PROCESS_STATE_BACKGROUND = 2;
+    public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
+    public static final int PROCESS_STATE_CACHED = 4;
+
+    public static final int PROCESS_STATE_COUNT = 5;
+
+    private static final String[] sProcessStateNames = new String[PROCESS_STATE_COUNT];
+
+    static {
+        // Assign individually to avoid future mismatch
+        sProcessStateNames[PROCESS_STATE_UNSPECIFIED] = "unspecified";
+        sProcessStateNames[PROCESS_STATE_FOREGROUND] = "fg";
+        sProcessStateNames[PROCESS_STATE_BACKGROUND] = "bg";
+        sProcessStateNames[PROCESS_STATE_FOREGROUND_SERVICE] = "fgs";
+        sProcessStateNames[PROCESS_STATE_CACHED] = "cached";
+    }
+
+    private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
+            POWER_COMPONENT_CPU,
+            POWER_COMPONENT_MOBILE_RADIO,
+            POWER_COMPONENT_WIFI,
+            POWER_COMPONENT_BLUETOOTH,
+    };
+
+    static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
+    static final int COLUMN_COUNT = 1;
+
+    /**
+     * Identifies power attribution dimensions that a caller is interested in.
+     */
+    public static final class Dimensions {
+        public final @PowerComponent int powerComponent;
+        public final @ProcessState int processState;
+
+        public Dimensions(int powerComponent, int processState) {
+            this.powerComponent = powerComponent;
+            this.processState = processState;
+        }
+
+        @Override
+        public String toString() {
+            boolean dimensionSpecified = false;
+            StringBuilder sb = new StringBuilder();
+            if (powerComponent != POWER_COMPONENT_ANY) {
+                sb.append("powerComponent=").append(sPowerComponentNames[powerComponent]);
+                dimensionSpecified = true;
+            }
+            if (processState != PROCESS_STATE_UNSPECIFIED) {
+                if (dimensionSpecified) {
+                    sb.append(", ");
+                }
+                sb.append("processState=").append(sProcessStateNames[processState]);
+                dimensionSpecified = true;
+            }
+            if (!dimensionSpecified) {
+                sb.append("any components and process states");
+            }
+            return sb.toString();
+        }
+    }
+
+    public static final Dimensions UNSPECIFIED_DIMENSIONS =
+            new Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_ANY);
+
+    /**
+     * Identifies power attribution dimensions that are captured by an data element of
+     * a BatteryConsumer. These Keys are used to access those values and to set them using
+     * Builders.  See for example {@link #getConsumedPower(Key)}.
+     *
+     * Keys cannot be allocated by the client - they can only be obtained by calling
+     * {@link #getKeys} or {@link #getKey}.  All BatteryConsumers that are part of the
+     * same BatteryUsageStats share the same set of keys, therefore it is safe to obtain
+     * the keys from one BatteryConsumer and apply them to other BatteryConsumers
+     * in the same BatteryUsageStats.
+     */
+    public static final class Key {
+        public final @PowerComponent int powerComponent;
+        public final @ProcessState int processState;
+
+        final int mPowerModelColumnIndex;
+        final int mPowerColumnIndex;
+        final int mDurationColumnIndex;
+        private String mShortString;
+
+        private Key(int powerComponent, int processState, int powerModelColumnIndex,
+                int powerColumnIndex, int durationColumnIndex) {
+            this.powerComponent = powerComponent;
+            this.processState = processState;
+
+            mPowerModelColumnIndex = powerModelColumnIndex;
+            mPowerColumnIndex = powerColumnIndex;
+            mDurationColumnIndex = durationColumnIndex;
+        }
+
+        @SuppressWarnings("EqualsUnsafeCast")
+        @Override
+        public boolean equals(Object o) {
+            // Skipping null and class check for performance
+            final Key key = (Key) o;
+            return powerComponent == key.powerComponent
+                && processState == key.processState;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = powerComponent;
+            result = 31 * result + processState;
+            return result;
+        }
+
+        /**
+         * Returns a string suitable for use in dumpsys.
+         */
+        public String toShortString() {
+            if (mShortString == null) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(powerComponentIdToString(powerComponent));
+                if (processState != PROCESS_STATE_UNSPECIFIED) {
+                    sb.append(':');
+                    sb.append(processStateToString(processState));
+                }
+                mShortString = sb.toString();
+            }
+            return mShortString;
+        }
+    }
+
+    protected final BatteryConsumerData mData;
+    protected final PowerComponents mPowerComponents;
+
+    protected BatteryConsumer(BatteryConsumerData data, @NonNull PowerComponents powerComponents) {
+        mData = data;
+        mPowerComponents = powerComponents;
+    }
+
+    public BatteryConsumer(BatteryConsumerData data) {
+        mData = data;
+        mPowerComponents = new PowerComponents(data);
+    }
+
+    /**
+     * Total power consumed by this consumer, in mAh.
+     */
+    public double getConsumedPower() {
+        return mPowerComponents.getConsumedPower(UNSPECIFIED_DIMENSIONS);
+    }
+
+    /**
+     * Returns power consumed aggregated over the specified dimensions, in mAh.
+     */
+    public double getConsumedPower(Dimensions dimensions) {
+        return mPowerComponents.getConsumedPower(dimensions);
+    }
+
+    /**
+     * Returns keys for various power values attributed to the specified component
+     * held by this BatteryUsageStats object.
+     */
+    public Key[] getKeys(@PowerComponent int componentId) {
+        return mData.getKeys(componentId);
+    }
+
+    /**
+     * Returns the key for the power attributed to the specified component,
+     * for all values of other dimensions such as process state.
+     */
+    public Key getKey(@PowerComponent int componentId) {
+        return mData.getKey(componentId, PROCESS_STATE_UNSPECIFIED);
+    }
+
+    /**
+     * Returns the key for the power attributed to the specified component and process state.
+     */
+    public Key getKey(@PowerComponent int componentId, @ProcessState int processState) {
+        return mData.getKey(componentId, processState);
+    }
+
+    /**
+     * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+     *
+     * @param componentId The ID of the power component, e.g.
+     *                    {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+     * @return Amount of consumed power in mAh.
+     */
+    public double getConsumedPower(@PowerComponent int componentId) {
+        return mPowerComponents.getConsumedPower(
+                mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
+    }
+
+    /**
+     * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+     *
+     * @param key The key of the power component, obtained by calling {@link #getKey} or
+     *            {@link #getKeys} method.
+     * @return Amount of consumed power in mAh.
+     */
+    public double getConsumedPower(@NonNull Key key) {
+        return mPowerComponents.getConsumedPower(key);
+    }
+
+    /**
+     * Returns the ID of the model that was used for power estimation.
+     *
+     * @param componentId The ID of the power component, e.g.
+     *                    {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+     */
+    public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) {
+        return mPowerComponents.getPowerModel(
+                mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
+    }
+
+    /**
+     * Returns the ID of the model that was used for power estimation.
+     *
+     * @param key The key of the power component, obtained by calling {@link #getKey} or
+     *            {@link #getKeys} method.
+     */
+    public @PowerModel int getPowerModel(@NonNull BatteryConsumer.Key key) {
+        return mPowerComponents.getPowerModel(key);
+    }
+
+    /**
+     * Returns the amount of drain attributed to the specified custom drain type.
+     *
+     * @param componentId The ID of the custom power component.
+     * @return Amount of consumed power in mAh.
+     */
+    public double getConsumedPowerForCustomComponent(int componentId) {
+        return mPowerComponents.getConsumedPowerForCustomComponent(componentId);
+    }
+
+    public int getCustomPowerComponentCount() {
+        return mData.layout.customPowerComponentCount;
+    }
+
+    /**
+     * Returns the name of the specified power component.
+     *
+     * @param componentId The ID of the custom power component.
+     */
+    public String getCustomPowerComponentName(int componentId) {
+        return mPowerComponents.getCustomPowerComponentName(componentId);
+    }
+
+    /**
+     * Returns the amount of time since BatteryStats reset used by the specified component, e.g.
+     * CPU, WiFi etc.
+     *
+     * @param componentId The ID of the power component, e.g.
+     *                    {@link UidBatteryConsumer#POWER_COMPONENT_CPU}.
+     * @return Amount of time in milliseconds.
+     */
+    public long getUsageDurationMillis(@PowerComponent int componentId) {
+        return mPowerComponents.getUsageDurationMillis(getKey(componentId));
+    }
+
+    /**
+     * Returns the amount of time since BatteryStats reset used by the specified component, e.g.
+     * CPU, WiFi etc.
+     *
+     *
+     * @param key The key of the power component, obtained by calling {@link #getKey} or
+     *            {@link #getKeys} method.
+     * @return Amount of time in milliseconds.
+     */
+    public long getUsageDurationMillis(@NonNull Key key) {
+        return mPowerComponents.getUsageDurationMillis(key);
+    }
+
+    /**
+     * Returns the amount of usage time attributed to the specified custom component
+     * since BatteryStats reset.
+     *
+     * @param componentId The ID of the custom power component.
+     * @return Amount of time in milliseconds.
+     */
+    public long getUsageDurationForCustomComponentMillis(int componentId) {
+        return mPowerComponents.getUsageDurationForCustomComponentMillis(componentId);
+    }
+
+    /**
+     * Returns the name of the specified component.  Intended for logging and debugging.
+     */
+    public static String powerComponentIdToString(@BatteryConsumer.PowerComponent int componentId) {
+        if (componentId == POWER_COMPONENT_ANY) {
+            return "all";
+        }
+        return sPowerComponentNames[componentId];
+    }
+
+    /**
+     * Returns the name of the specified power model.  Intended for logging and debugging.
+     */
+    public static String powerModelToString(@BatteryConsumer.PowerModel int powerModel) {
+        switch (powerModel) {
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
+                return "energy consumption";
+            case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
+                return "power profile";
+            default:
+                return "";
+        }
+    }
+
+    /**
+     * Returns the equivalent PowerModel enum for the specified power model.
+     * {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel}
+     */
+    public static int powerModelToProtoEnum(@BatteryConsumer.PowerModel int powerModel) {
+        switch (powerModel) {
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
+                return BatteryUsageStatsAtomsProto.PowerComponentModel.MEASURED_ENERGY;
+            case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
+                return BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_PROFILE;
+            default:
+                return BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED;
+        }
+    }
+
+    /**
+     * Returns the name of the specified process state.  Intended for logging and debugging.
+     */
+    public static String processStateToString(@BatteryConsumer.ProcessState int processState) {
+        return sProcessStateNames[processState];
+    }
+
+    /**
+     * Prints the stats in a human-readable format.
+     */
+    public void dump(PrintWriter pw) {
+        dump(pw, true);
+    }
+
+    /**
+     * Prints the stats in a human-readable format.
+     *
+     * @param skipEmptyComponents if true, omit any power components with a zero amount.
+     */
+    public abstract void dump(PrintWriter pw, boolean skipEmptyComponents);
+
+    /** Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto. */
+    boolean hasStatsProtoData() {
+        return writeStatsProtoImpl(null, /* Irrelevant fieldId: */ 0);
+    }
+
+    /** Writes the atoms.proto BATTERY_CONSUMER_DATA for this BatteryConsumer to the given proto. */
+    void writeStatsProto(@NonNull ProtoOutputStream proto, long fieldId) {
+        writeStatsProtoImpl(proto, fieldId);
+    }
+
+    /**
+     * Returns whether there are any atoms.proto BATTERY_CONSUMER_DATA data to write to a proto,
+     * and writes it to the given proto if it is non-null.
+     */
+    private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto, long fieldId) {
+        final long totalConsumedPowerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower());
+
+        if (totalConsumedPowerDeciCoulombs == 0) {
+            // NOTE: Strictly speaking we should also check !mPowerComponents.hasStatsProtoData().
+            // However, that call is a bit expensive (a for loop). And the only way that
+            // totalConsumedPower can be 0 while mPowerComponents.hasStatsProtoData() is true is
+            // if POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS (which is the only negative
+            // allowed) happens to exactly equal the sum of all other components, which
+            // can't really happen in practice.
+            // So we'll just adopt the rule "if total==0, don't write any details".
+            // If negative values are used for other things in the future, this can be revisited.
+            return false;
+        }
+        if (proto == null) {
+            // We're just asked whether there is data, not to actually write it. And there is.
+            return true;
+        }
+
+        final long token = proto.start(fieldId);
+        proto.write(
+                BatteryUsageStatsAtomsProto.BatteryConsumerData.TOTAL_CONSUMED_POWER_DECI_COULOMBS,
+                totalConsumedPowerDeciCoulombs);
+        mPowerComponents.writeStatsProto(proto);
+        proto.end(token);
+
+        return true;
+    }
+
+    /** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */
+    static long convertMahToDeciCoulombs(double powerMah) {
+        return (long) (powerMah * (10 * 3600 / 1000) + 0.5);
+    }
+
+    static class BatteryConsumerData {
+        private final CursorWindow mCursorWindow;
+        private final int mCursorRow;
+        public final BatteryConsumerDataLayout layout;
+
+        BatteryConsumerData(CursorWindow cursorWindow, int cursorRow,
+                BatteryConsumerDataLayout layout) {
+            mCursorWindow = cursorWindow;
+            mCursorRow = cursorRow;
+            this.layout = layout;
+        }
+
+        @Nullable
+        static BatteryConsumerData create(CursorWindow cursorWindow,
+                BatteryConsumerDataLayout layout) {
+            int cursorRow = cursorWindow.getNumRows();
+            if (!cursorWindow.allocRow()) {
+                Slog.e(TAG, "Cannot allocate BatteryConsumerData: too many UIDs: " + cursorRow);
+                cursorRow = -1;
+            }
+            return new BatteryConsumerData(cursorWindow, cursorRow, layout);
+        }
+
+        public Key[] getKeys(int componentId) {
+            return layout.keys[componentId];
+        }
+
+        Key getKeyOrThrow(int componentId, int processState) {
+            Key key = getKey(componentId, processState);
+            if (key == null) {
+                if (processState == PROCESS_STATE_ANY) {
+                    throw new IllegalArgumentException(
+                            "Unsupported power component ID: " + componentId);
+                } else {
+                    throw new IllegalArgumentException(
+                            "Unsupported power component ID: " + componentId
+                                    + " process state: " + processState);
+                }
+            }
+            return key;
+        }
+
+        Key getKey(int componentId, int processState) {
+            if (componentId >= POWER_COMPONENT_COUNT) {
+                return null;
+            }
+
+            if (processState == PROCESS_STATE_ANY) {
+                // The 0-th key for each component corresponds to the roll-up,
+                // across all dimensions. We might as well skip the iteration over the array.
+                return layout.keys[componentId][0];
+            } else {
+                for (Key key : layout.keys[componentId]) {
+                    if (key.processState == processState) {
+                        return key;
+                    }
+                }
+            }
+            return null;
+        }
+
+        void putInt(int columnIndex, int value) {
+            if (mCursorRow == -1) {
+                return;
+            }
+            mCursorWindow.putLong(value, mCursorRow, columnIndex);
+        }
+
+        int getInt(int columnIndex) {
+            if (mCursorRow == -1) {
+                return 0;
+            }
+            return mCursorWindow.getInt(mCursorRow, columnIndex);
+        }
+
+        void putDouble(int columnIndex, double value) {
+            if (mCursorRow == -1) {
+                return;
+            }
+            mCursorWindow.putDouble(value, mCursorRow, columnIndex);
+        }
+
+        double getDouble(int columnIndex) {
+            if (mCursorRow == -1) {
+                return 0;
+            }
+            return mCursorWindow.getDouble(mCursorRow, columnIndex);
+        }
+
+        void putLong(int columnIndex, long value) {
+            if (mCursorRow == -1) {
+                return;
+            }
+            mCursorWindow.putLong(value, mCursorRow, columnIndex);
+        }
+
+        long getLong(int columnIndex) {
+            if (mCursorRow == -1) {
+                return 0;
+            }
+            return mCursorWindow.getLong(mCursorRow, columnIndex);
+        }
+
+        void putString(int columnIndex, String value) {
+            if (mCursorRow == -1) {
+                return;
+            }
+            mCursorWindow.putString(value, mCursorRow, columnIndex);
+        }
+
+        String getString(int columnIndex) {
+            if (mCursorRow == -1) {
+                return null;
+            }
+            return mCursorWindow.getString(mCursorRow, columnIndex);
+        }
+    }
+
+    static class BatteryConsumerDataLayout {
+        private static final Key[] KEY_ARRAY = new Key[0];
+        public final String[] customPowerComponentNames;
+        public final int customPowerComponentCount;
+        public final boolean powerModelsIncluded;
+        public final boolean processStateDataIncluded;
+        public final Key[][] keys;
+        public final int totalConsumedPowerColumnIndex;
+        public final int firstCustomConsumedPowerColumn;
+        public final int firstCustomUsageDurationColumn;
+        public final int columnCount;
+        public final Key[][] processStateKeys;
+
+        private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames,
+                boolean powerModelsIncluded, boolean includeProcessStateData) {
+            this.customPowerComponentNames = customPowerComponentNames;
+            this.customPowerComponentCount = customPowerComponentNames.length;
+            this.powerModelsIncluded = powerModelsIncluded;
+            this.processStateDataIncluded = includeProcessStateData;
+
+            int columnIndex = firstColumn;
+
+            totalConsumedPowerColumnIndex = columnIndex++;
+
+            keys = new Key[POWER_COMPONENT_COUNT][];
+
+            ArrayList<Key> perComponentKeys = new ArrayList<>();
+            for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) {
+                perComponentKeys.clear();
+
+                // Declare the Key for the power component, ignoring other dimensions.
+                perComponentKeys.add(
+                        new Key(componentId, PROCESS_STATE_ANY,
+                                powerModelsIncluded ? columnIndex++ : -1,  // power model
+                                columnIndex++,      // power
+                                columnIndex++       // usage duration
+                        ));
+
+                // Declare Keys for all process states, if needed
+                if (includeProcessStateData) {
+                    boolean isSupported = false;
+                    for (int id : SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE) {
+                        if (id == componentId) {
+                            isSupported = true;
+                            break;
+                        }
+                    }
+                    if (isSupported) {
+                        for (int processState = 0; processState < PROCESS_STATE_COUNT;
+                                processState++) {
+                            if (processState == PROCESS_STATE_UNSPECIFIED) {
+                                continue;
+                            }
+
+                            perComponentKeys.add(
+                                    new Key(componentId, processState,
+                                            powerModelsIncluded ? columnIndex++ : -1, // power model
+                                            columnIndex++,      // power
+                                            columnIndex++       // usage duration
+                                    ));
+                        }
+                    }
+                }
+
+                keys[componentId] = perComponentKeys.toArray(KEY_ARRAY);
+            }
+
+            if (includeProcessStateData) {
+                processStateKeys = new Key[BatteryConsumer.PROCESS_STATE_COUNT][];
+                ArrayList<Key> perProcStateKeys = new ArrayList<>();
+                for (int processState = 0; processState < PROCESS_STATE_COUNT; processState++) {
+                    if (processState == PROCESS_STATE_UNSPECIFIED) {
+                        continue;
+                    }
+
+                    perProcStateKeys.clear();
+                    for (int i = 0; i < keys.length; i++) {
+                        for (int j = 0; j < keys[i].length; j++) {
+                            if (keys[i][j].processState == processState) {
+                                perProcStateKeys.add(keys[i][j]);
+                            }
+                        }
+                    }
+                    processStateKeys[processState] = perProcStateKeys.toArray(KEY_ARRAY);
+                }
+            } else {
+                processStateKeys = null;
+            }
+
+            firstCustomConsumedPowerColumn = columnIndex;
+            columnIndex += customPowerComponentCount;
+
+            firstCustomUsageDurationColumn = columnIndex;
+            columnIndex += customPowerComponentCount;
+
+            columnCount = columnIndex;
+        }
+    }
+
+    static BatteryConsumerDataLayout createBatteryConsumerDataLayout(
+            String[] customPowerComponentNames, boolean includePowerModels,
+            boolean includeProcessStateData) {
+        int columnCount = BatteryConsumer.COLUMN_COUNT;
+        columnCount = Math.max(columnCount, AggregateBatteryConsumer.COLUMN_COUNT);
+        columnCount = Math.max(columnCount, UidBatteryConsumer.COLUMN_COUNT);
+        columnCount = Math.max(columnCount, UserBatteryConsumer.COLUMN_COUNT);
+
+        return new BatteryConsumerDataLayout(columnCount, customPowerComponentNames,
+                includePowerModels, includeProcessStateData);
+    }
+
+    protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
+        protected final BatteryConsumer.BatteryConsumerData mData;
+        protected final PowerComponents.Builder mPowerComponentsBuilder;
+
+        public BaseBuilder(BatteryConsumer.BatteryConsumerData data, int consumerType) {
+            mData = data;
+            data.putLong(COLUMN_INDEX_BATTERY_CONSUMER_TYPE, consumerType);
+
+            mPowerComponentsBuilder = new PowerComponents.Builder(data);
+        }
+
+        @Nullable
+        public Key[] getKeys(@PowerComponent int componentId) {
+            return mData.getKeys(componentId);
+        }
+
+        @Nullable
+        public Key getKey(@PowerComponent int componentId, @ProcessState int processState) {
+            return mData.getKey(componentId, processState);
+        }
+
+        /**
+         * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+         *
+         * @param componentId    The ID of the power component, e.g.
+         *                       {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+         * @param componentPower Amount of consumed power in mAh.
+         */
+        @NonNull
+        public T setConsumedPower(@PowerComponent int componentId, double componentPower) {
+            return setConsumedPower(componentId, componentPower, POWER_MODEL_POWER_PROFILE);
+        }
+
+        /**
+         * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+         *
+         * @param componentId    The ID of the power component, e.g.
+         *                       {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+         * @param componentPower Amount of consumed power in mAh.
+         */
+        @SuppressWarnings("unchecked")
+        @NonNull
+        public T setConsumedPower(@PowerComponent int componentId, double componentPower,
+                @PowerModel int powerModel) {
+            mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+                    componentPower, powerModel);
+            return (T) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        @NonNull
+        public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
+            mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel);
+            return (T) this;
+        }
+
+        /**
+         * Sets the amount of drain attributed to the specified custom drain type.
+         *
+         * @param componentId    The ID of the custom power component.
+         * @param componentPower Amount of consumed power in mAh.
+         */
+        @SuppressWarnings("unchecked")
+        @NonNull
+        public T setConsumedPowerForCustomComponent(int componentId, double componentPower) {
+            mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower);
+            return (T) this;
+        }
+
+        /**
+         * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc.
+         *
+         * @param componentId              The ID of the power component, e.g.
+         *                                 {@link UidBatteryConsumer#POWER_COMPONENT_CPU}.
+         * @param componentUsageTimeMillis Amount of time in microseconds.
+         */
+        @SuppressWarnings("unchecked")
+        @NonNull
+        public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId,
+                long componentUsageTimeMillis) {
+            mPowerComponentsBuilder
+                    .setUsageDurationMillis(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+                            componentUsageTimeMillis);
+            return (T) this;
+        }
+
+
+        @SuppressWarnings("unchecked")
+        @NonNull
+        public T setUsageDurationMillis(Key key, long componentUsageTimeMillis) {
+            mPowerComponentsBuilder.setUsageDurationMillis(key, componentUsageTimeMillis);
+            return (T) this;
+        }
+
+        /**
+         * Sets the amount of time used by the specified custom component.
+         *
+         * @param componentId              The ID of the custom power component.
+         * @param componentUsageTimeMillis Amount of time in microseconds.
+         */
+        @SuppressWarnings("unchecked")
+        @NonNull
+        public T setUsageDurationForCustomComponentMillis(int componentId,
+                long componentUsageTimeMillis) {
+            mPowerComponentsBuilder.setUsageDurationForCustomComponentMillis(componentId,
+                    componentUsageTimeMillis);
+            return (T) this;
+        }
+
+        /**
+         * Returns the total power accumulated by this builder so far. It may change
+         * by the time the {@code build()} method is called.
+         */
+        public double getTotalPower() {
+            return mPowerComponentsBuilder.getTotalPower();
+        }
+    }
+}
diff --git a/android-34/android/os/BatteryManager.java b/android-34/android/os/BatteryManager.java
new file mode 100644
index 0000000..6bc0f6e
--- /dev/null
+++ b/android-34/android/os/BatteryManager.java
@@ -0,0 +1,514 @@
+/*
+ * 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.
+ * 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.os;
+
+import android.Manifest.permission;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.health.V1_0.Constants;
+
+import com.android.internal.app.IBatteryStats;
+
+/**
+ * The BatteryManager class contains strings and constants used for values
+ * in the {@link android.content.Intent#ACTION_BATTERY_CHANGED} Intent, and
+ * provides a method for querying battery and charging properties.
+ */
+@SystemService(Context.BATTERY_SERVICE)
+public class BatteryManager {
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the current status constant.
+     */
+    public static final String EXTRA_STATUS = "status";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the current health constant.
+     */
+    public static final String EXTRA_HEALTH = "health";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * boolean indicating whether a battery is present.
+     */
+    public static final String EXTRA_PRESENT = "present";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer field containing the current battery level, from 0 to
+     * {@link #EXTRA_SCALE}.
+     */
+    public static final String EXTRA_LEVEL = "level";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Boolean field indicating whether the battery is currently considered to be
+     * low, that is whether a {@link Intent#ACTION_BATTERY_LOW} broadcast
+     * has been sent.
+     */
+    public static final String EXTRA_BATTERY_LOW = "battery_low";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the maximum battery level.
+     */
+    public static final String EXTRA_SCALE = "scale";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the resource ID of a small status bar icon
+     * indicating the current battery state.
+     */
+    public static final String EXTRA_ICON_SMALL = "icon-small";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer indicating whether the device is plugged in to a power
+     * source; 0 means it is on battery, other constants are different
+     * types of power sources.
+     */
+    public static final String EXTRA_PLUGGED = "plugged";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the current battery voltage level.
+     */
+    public static final String EXTRA_VOLTAGE = "voltage";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the current battery temperature.
+     */
+    public static final String EXTRA_TEMPERATURE = "temperature";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * String describing the technology of the current battery.
+     */
+    public static final String EXTRA_TECHNOLOGY = "technology";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Int value set to nonzero if an unsupported charger is attached
+     * to the device.
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public static final String EXTRA_INVALID_CHARGER = "invalid_charger";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Int value set to the maximum charging current supported by the charger in micro amperes.
+     * {@hide}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final String EXTRA_MAX_CHARGING_CURRENT = "max_charging_current";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Int value set to the maximum charging voltage supported by the charger in micro volts.
+     * {@hide}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final String EXTRA_MAX_CHARGING_VOLTAGE = "max_charging_voltage";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the charge counter present in the battery.
+     * {@hide}
+     */
+     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+     public static final String EXTRA_CHARGE_COUNTER = "charge_counter";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Current int sequence number of the update.
+     * {@hide}
+     */
+    public static final String EXTRA_SEQUENCE = "seq";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Int value representing the battery charging cycle count.
+     */
+    public static final String EXTRA_CYCLE_COUNT = "android.os.extra.CYCLE_COUNT";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Int value representing the battery charging status.
+     */
+    public static final String EXTRA_CHARGING_STATUS = "android.os.extra.CHARGING_STATUS";
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}:
+     * Contains list of Bundles representing battery events
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EVENTS = "android.os.extra.EVENTS";
+
+    /**
+     * Extra for event in {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}:
+     * Long value representing time when event occurred as returned by
+     * {@link android.os.SystemClock#elapsedRealtime()}
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP";
+
+    // values for "status" field in the ACTION_BATTERY_CHANGED Intent
+    public static final int BATTERY_STATUS_UNKNOWN = Constants.BATTERY_STATUS_UNKNOWN;
+    public static final int BATTERY_STATUS_CHARGING = Constants.BATTERY_STATUS_CHARGING;
+    public static final int BATTERY_STATUS_DISCHARGING = Constants.BATTERY_STATUS_DISCHARGING;
+    public static final int BATTERY_STATUS_NOT_CHARGING = Constants.BATTERY_STATUS_NOT_CHARGING;
+    public static final int BATTERY_STATUS_FULL = Constants.BATTERY_STATUS_FULL;
+
+    // values for "health" field in the ACTION_BATTERY_CHANGED Intent
+    public static final int BATTERY_HEALTH_UNKNOWN = Constants.BATTERY_HEALTH_UNKNOWN;
+    public static final int BATTERY_HEALTH_GOOD = Constants.BATTERY_HEALTH_GOOD;
+    public static final int BATTERY_HEALTH_OVERHEAT = Constants.BATTERY_HEALTH_OVERHEAT;
+    public static final int BATTERY_HEALTH_DEAD = Constants.BATTERY_HEALTH_DEAD;
+    public static final int BATTERY_HEALTH_OVER_VOLTAGE = Constants.BATTERY_HEALTH_OVER_VOLTAGE;
+    public static final int BATTERY_HEALTH_UNSPECIFIED_FAILURE = Constants.BATTERY_HEALTH_UNSPECIFIED_FAILURE;
+    public static final int BATTERY_HEALTH_COLD = Constants.BATTERY_HEALTH_COLD;
+
+    // values of the "plugged" field in the ACTION_BATTERY_CHANGED intent.
+    // These must be powers of 2.
+    /** Power source is an AC charger. */
+    public static final int BATTERY_PLUGGED_AC = OsProtoEnums.BATTERY_PLUGGED_AC; // = 1
+    /** Power source is a USB port. */
+    public static final int BATTERY_PLUGGED_USB = OsProtoEnums.BATTERY_PLUGGED_USB; // = 2
+    /** Power source is wireless. */
+    public static final int BATTERY_PLUGGED_WIRELESS = OsProtoEnums.BATTERY_PLUGGED_WIRELESS; // = 4
+    /** Power source is dock. */
+    public static final int BATTERY_PLUGGED_DOCK = OsProtoEnums.BATTERY_PLUGGED_DOCK; // = 8
+
+    // values for "charge policy" property
+    /**
+     * Default policy (e.g. normal).
+     * @hide
+     */
+    @SystemApi
+    public static final int CHARGING_POLICY_DEFAULT = OsProtoEnums.CHARGING_POLICY_DEFAULT; // = 1
+    /**
+     * Optimized for battery health using static thresholds (e.g stop at 80%).
+     * @hide
+     */
+    @SystemApi
+    public static final int CHARGING_POLICY_ADAPTIVE_AON =
+                                            OsProtoEnums.CHARGING_POLICY_ADAPTIVE_AON; // = 2
+    /**
+     * Optimized for battery health using adaptive thresholds.
+     * @hide
+     */
+    @SystemApi
+    public static final int CHARGING_POLICY_ADAPTIVE_AC =
+                                            OsProtoEnums.CHARGING_POLICY_ADAPTIVE_AC; // = 3
+    /**
+     * Optimized for battery health, devices always connected to power.
+     * @hide
+     */
+    @SystemApi
+    public static final int CHARGING_POLICY_ADAPTIVE_LONGLIFE =
+                                            OsProtoEnums.CHARGING_POLICY_ADAPTIVE_LONGLIFE; // = 4
+
+    /** @hide */
+    public static final int BATTERY_PLUGGED_ANY =
+            BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS
+                    | BATTERY_PLUGGED_DOCK;
+
+    /**
+     * Sent when the device's battery has started charging (or has reached full charge
+     * and the device is on power).  This is a good time to do work that you would like to
+     * avoid doing while on battery (that is to avoid draining the user's battery due to
+     * things they don't care enough about).
+     *
+     * This is paired with {@link #ACTION_DISCHARGING}.  The current state can always
+     * be retrieved with {@link #isCharging()}.
+     */
+    public static final String ACTION_CHARGING = "android.os.action.CHARGING";
+
+    /**
+     * Sent when the device's battery may be discharging, so apps should avoid doing
+     * extraneous work that would cause it to discharge faster.
+     *
+     * This is paired with {@link #ACTION_CHARGING}.  The current state can always
+     * be retrieved with {@link #isCharging()}.
+     */
+    public static final String ACTION_DISCHARGING = "android.os.action.DISCHARGING";
+
+    /*
+     * Battery property identifiers.  These must match the values in
+     * frameworks/native/include/batteryservice/BatteryService.h
+     */
+    /** Battery capacity in microampere-hours, as an integer. */
+    public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1;
+
+    /**
+     * Instantaneous battery current in microamperes, as an integer.  Positive
+     * values indicate net current entering the battery from a charge source,
+     * negative values indicate net current discharging from the battery.
+     */
+    public static final int BATTERY_PROPERTY_CURRENT_NOW = 2;
+
+    /**
+     * Average battery current in microamperes, as an integer.  Positive
+     * values indicate net current entering the battery from a charge source,
+     * negative values indicate net current discharging from the battery.
+     * The time period over which the average is computed may depend on the
+     * fuel gauge hardware and its configuration.
+     */
+    public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3;
+
+    /**
+     * Remaining battery capacity as an integer percentage of total capacity
+     * (with no fractional part).
+     */
+    public static final int BATTERY_PROPERTY_CAPACITY = 4;
+
+    /**
+     * Battery remaining energy in nanowatt-hours, as a long integer.
+     */
+    public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5;
+
+    /**
+     * Battery charge status, from a BATTERY_STATUS_* value.
+     */
+    public static final int BATTERY_PROPERTY_STATUS = 6;
+
+    /**
+     * Battery manufacturing date is reported in epoch. The 0 timepoint
+     * begins at midnight Coordinated Universal Time (UTC) on January 1, 1970.
+     * It is a long integer in seconds.
+     *
+     * <p class="note">
+     * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * Example: <code>
+     *  // The value returned from the API can be used to create a Date, used
+     *  // to set the time on a calendar and coverted to a string.
+     *  import java.util.Date;
+     *
+     *  mBatteryManager = mContext.getSystemService(BatteryManager.class);
+     *  final long manufacturingDate =
+     *      mBatteryManager.getLongProperty(BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE);
+     *  Date date = new Date(manufacturingDate);
+     *  Calendar calendar = Calendar.getInstance();
+     *  calendar.setTime(date);
+     * // Convert to yyyy-MM-dd HH:mm:ss format string
+     *  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+     *  String dateString = sdf.format(date);
+     * </code>
+     * @hide
+     */
+    @RequiresPermission(permission.BATTERY_STATS)
+    @SystemApi
+    public static final int BATTERY_PROPERTY_MANUFACTURING_DATE = 7;
+
+    /**
+     * The date of first usage is reported in epoch. The 0 timepoint
+     * begins at midnight Coordinated Universal Time (UTC) on January 1, 1970.
+     * It is a long integer in seconds.
+     *
+     * <p class="note">
+     * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * {@link BATTERY_PROPERTY_MANUFACTURING_DATE for sample code}
+     * @hide
+     */
+    @RequiresPermission(permission.BATTERY_STATS)
+    @SystemApi
+    public static final int BATTERY_PROPERTY_FIRST_USAGE_DATE = 8;
+
+    /**
+     * Battery charging policy from a CHARGING_POLICY_* value..
+     *
+     * <p class="note">
+     * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * @hide
+     */
+    @RequiresPermission(permission.BATTERY_STATS)
+    @SystemApi
+    public static final int BATTERY_PROPERTY_CHARGING_POLICY = 9;
+
+    /**
+     *
+     * Percentage representing the measured battery state of health (remaining
+     * estimated full charge capacity relative to the rated capacity in %).
+     *
+     * <p class="note">
+     * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * @hide
+     */
+    @RequiresPermission(permission.BATTERY_STATS)
+    @SystemApi
+    public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10;
+
+    private final Context mContext;
+    private final IBatteryStats mBatteryStats;
+    private final IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
+
+    /**
+     * @removed Was previously made visible by accident.
+     */
+    public BatteryManager() {
+        mContext = null;
+        mBatteryStats = IBatteryStats.Stub.asInterface(
+                ServiceManager.getService(BatteryStats.SERVICE_NAME));
+        mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(
+                ServiceManager.getService("batteryproperties"));
+    }
+
+    /** {@hide} */
+    public BatteryManager(Context context,
+            IBatteryStats batteryStats,
+            IBatteryPropertiesRegistrar batteryPropertiesRegistrar) {
+        mContext = context;
+        mBatteryStats = batteryStats;
+        mBatteryPropertiesRegistrar = batteryPropertiesRegistrar;
+    }
+
+    /**
+     * Return true if the battery is currently considered to be charging.  This means that
+     * the device is plugged in and is supplying sufficient power that the battery level is
+     * going up (or the battery is fully charged).  Changes in this state are matched by
+     * broadcasts of {@link #ACTION_CHARGING} and {@link #ACTION_DISCHARGING}.
+     */
+    public boolean isCharging() {
+        try {
+            return mBatteryStats.isCharging();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Query a battery property from the batteryproperties service.
+     *
+     * Returns the requested value, or Long.MIN_VALUE if property not
+     * supported on this system or on other error.
+     */
+    private long queryProperty(int id) {
+        long ret;
+
+        if (mBatteryPropertiesRegistrar == null) {
+            return Long.MIN_VALUE;
+        }
+
+        try {
+            BatteryProperty prop = new BatteryProperty();
+            if (mBatteryPropertiesRegistrar.getProperty(id, prop) == 0)
+                ret = prop.getLong();
+            else
+                ret = Long.MIN_VALUE;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return ret;
+    }
+
+    /**
+     * Return the value of a battery property of integer type.
+     *
+     * @param id identifier of the requested property
+     *
+     * @return the property value. If the property is not supported or there is any other error,
+     *    return (a) 0 if {@code targetSdkVersion < VERSION_CODES.P} or (b) Integer.MIN_VALUE
+     *    if {@code targetSdkVersion >= VERSION_CODES.P}.
+     */
+    public int getIntProperty(int id) {
+        long value = queryProperty(id);
+        if (value == Long.MIN_VALUE && mContext != null
+                && mContext.getApplicationInfo().targetSdkVersion
+                    >= android.os.Build.VERSION_CODES.P) {
+            return Integer.MIN_VALUE;
+        }
+
+        return (int) value;
+    }
+
+    /**
+     * Return the value of a battery property of long type If the
+     * platform does not provide the property queried, this value will
+     * be Long.MIN_VALUE.
+     *
+     * @param id identifier of the requested property
+     *
+     * @return the property value, or Long.MIN_VALUE if not supported.
+     */
+    public long getLongProperty(int id) {
+        return queryProperty(id);
+    }
+
+    /**
+     * Return true if the plugType given is wired
+     * @param plugType {@link #BATTERY_PLUGGED_AC}, {@link #BATTERY_PLUGGED_USB},
+     *        or {@link #BATTERY_PLUGGED_WIRELESS}
+     *
+     * @return true if plugType is wired
+     * @hide
+     */
+    public static boolean isPlugWired(int plugType) {
+        return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC;
+    }
+
+    /**
+     * Compute an approximation for how much time (in milliseconds) remains until the battery is
+     * fully charged. Returns -1 if no time can be computed: either there is not enough current
+     * data to make a decision or the battery is currently discharging.
+     *
+     * @return how much time is left, in milliseconds, until the battery is fully charged or -1 if
+     *         the computation fails
+     */
+    public long computeChargeTimeRemaining() {
+        try {
+            return mBatteryStats.computeChargeTimeRemaining();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the delay for reporting battery state as charging after device is plugged in.
+     * This allows machine-learning or heuristics to delay the reporting and the corresponding
+     * broadcast, based on battery level, charging rate, and/or other parameters.
+     *
+     * @param delayMillis the delay in milliseconds, negative value to reset.
+     *
+     * @return True if the delay was set successfully.
+     *
+     * @see ACTION_CHARGING
+     * @hide
+     */
+    @RequiresPermission(permission.POWER_SAVER)
+    @SystemApi
+    public boolean setChargingStateUpdateDelayMillis(int delayMillis) {
+        try {
+            return mBatteryStats.setChargingStateUpdateDelayMillis(delayMillis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-34/android/os/BatteryManagerInternal.java b/android-34/android/os/BatteryManagerInternal.java
new file mode 100644
index 0000000..9bad0de
--- /dev/null
+++ b/android-34/android/os/BatteryManagerInternal.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 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.os;
+
+/**
+ * Battery manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class BatteryManagerInternal {
+    /**
+     * Returns true if the device is plugged into any of the specified plug types.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     */
+    public abstract boolean isPowered(int plugTypeSet);
+
+    /**
+     * Returns the current plug type.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     */
+    public abstract int getPlugType();
+
+    /**
+     * Returns battery level as a percentage.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     */
+    public abstract int getBatteryLevel();
+
+    /**
+     * Returns battery health status as an integer representing the current battery health constant.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     */
+    public abstract int getBatteryHealth();
+
+    /**
+     * Instantaneous battery capacity in uA-h, as defined in the HealthInfo HAL struct.
+     * Please note apparently it could be bigger than {@link #getBatteryFullCharge}.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     *
+     * @see android.hardware.health.V1_0.HealthInfo#batteryChargeCounter
+     */
+    public abstract int getBatteryChargeCounter();
+
+    /**
+     * Battery charge value when it is considered to be "full" in uA-h , as defined in the
+     * HealthInfo HAL struct.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     *
+     * @see android.hardware.health.V1_0.HealthInfo#batteryFullCharge
+     */
+    public abstract int getBatteryFullCharge();
+
+    /**
+     * Returns whether we currently consider the battery level to be low.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     */
+    public abstract boolean getBatteryLevelLow();
+
+    /**
+     * Returns a non-zero value if an unsupported charger is attached.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     */
+    public abstract int getInvalidCharger();
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     */
+    public abstract void setChargerAcOnline(boolean online, boolean forceUpdate);
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     */
+    public abstract void setBatteryLevel(int level, boolean forceUpdate);
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     */
+    public abstract void unplugBattery(boolean forceUpdate);
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     */
+    public abstract void resetBattery(boolean forceUpdate);
+
+    /**
+     * Suspend charging even if plugged in.
+     */
+    public abstract void suspendBatteryInput();
+}
diff --git a/android-34/android/os/BatteryProperty.java b/android-34/android/os/BatteryProperty.java
new file mode 100644
index 0000000..b40988a
--- /dev/null
+++ b/android-34/android/os/BatteryProperty.java
@@ -0,0 +1,84 @@
+/* Copyright 2013, 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.os;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Battery properties that may be queried using
+ * BatteryManager.getProperty()}
+ */
+
+/**
+ * @hide
+ */
+public class BatteryProperty implements Parcelable {
+    private long mValueLong;
+
+    /**
+     * @hide
+     */
+    public BatteryProperty() {
+        mValueLong = Long.MIN_VALUE;
+    }
+
+    /**
+     * @hide
+     */
+    public long getLong() {
+        return mValueLong;
+    }
+
+    /**
+     * @hide
+     */
+    public void setLong(long val) {
+        mValueLong = val;
+    }
+
+    /*
+     * Parcel read/write code must be kept in sync with
+     * frameworks/native/services/batteryservice/BatteryProperty.cpp
+     */
+
+    private BatteryProperty(Parcel p) {
+        readFromParcel(p);
+    }
+
+    public void readFromParcel(Parcel p) {
+        mValueLong = p.readLong();
+    }
+
+    public void writeToParcel(Parcel p, int flags) {
+        p.writeLong(mValueLong);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<BatteryProperty> CREATOR
+        = new Parcelable.Creator<BatteryProperty>() {
+        public BatteryProperty createFromParcel(Parcel p) {
+            return new BatteryProperty(p);
+        }
+
+        public BatteryProperty[] newArray(int size) {
+            return new BatteryProperty[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-34/android/os/BatterySaverPolicyConfig.java b/android-34/android/os/BatterySaverPolicyConfig.java
new file mode 100644
index 0000000..a999e65
--- /dev/null
+++ b/android-34/android/os/BatterySaverPolicyConfig.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Config to set Battery Saver policy flags.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BatterySaverPolicyConfig implements Parcelable {
+    private final float mAdjustBrightnessFactor;
+    private final boolean mAdvertiseIsEnabled;
+    private final boolean mDeferFullBackup;
+    private final boolean mDeferKeyValueBackup;
+    @NonNull
+    private final Map<String, String> mDeviceSpecificSettings;
+    private final boolean mDisableAnimation;
+    private final boolean mDisableAod;
+    private final boolean mDisableLaunchBoost;
+    private final boolean mDisableOptionalSensors;
+    private final boolean mDisableVibration;
+    private final boolean mEnableAdjustBrightness;
+    private final boolean mEnableDataSaver;
+    private final boolean mEnableFirewall;
+    private final boolean mEnableNightMode;
+    private final boolean mEnableQuickDoze;
+    private final boolean mForceAllAppsStandby;
+    private final boolean mForceBackgroundCheck;
+    private final int mLocationMode;
+    private final int mSoundTriggerMode;
+
+    private BatterySaverPolicyConfig(Builder in) {
+        mAdjustBrightnessFactor = Math.max(0, Math.min(in.mAdjustBrightnessFactor, 1f));
+        mAdvertiseIsEnabled = in.mAdvertiseIsEnabled;
+        mDeferFullBackup = in.mDeferFullBackup;
+        mDeferKeyValueBackup = in.mDeferKeyValueBackup;
+        mDeviceSpecificSettings = Collections.unmodifiableMap(
+                new ArrayMap<>(in.mDeviceSpecificSettings));
+        mDisableAnimation = in.mDisableAnimation;
+        mDisableAod = in.mDisableAod;
+        mDisableLaunchBoost = in.mDisableLaunchBoost;
+        mDisableOptionalSensors = in.mDisableOptionalSensors;
+        mDisableVibration = in.mDisableVibration;
+        mEnableAdjustBrightness = in.mEnableAdjustBrightness;
+        mEnableDataSaver = in.mEnableDataSaver;
+        mEnableFirewall = in.mEnableFirewall;
+        mEnableNightMode = in.mEnableNightMode;
+        mEnableQuickDoze = in.mEnableQuickDoze;
+        mForceAllAppsStandby = in.mForceAllAppsStandby;
+        mForceBackgroundCheck = in.mForceBackgroundCheck;
+        mLocationMode = Math.max(PowerManager.MIN_LOCATION_MODE,
+                Math.min(in.mLocationMode, PowerManager.MAX_LOCATION_MODE));
+        mSoundTriggerMode = Math.max(PowerManager.MIN_SOUND_TRIGGER_MODE,
+                Math.min(in.mSoundTriggerMode, PowerManager.MAX_SOUND_TRIGGER_MODE));
+    }
+
+    private BatterySaverPolicyConfig(Parcel in) {
+        mAdjustBrightnessFactor = Math.max(0, Math.min(in.readFloat(), 1f));
+        mAdvertiseIsEnabled = in.readBoolean();
+        mDeferFullBackup = in.readBoolean();
+        mDeferKeyValueBackup = in.readBoolean();
+
+        final int size = in.readInt();
+        Map<String, String> deviceSpecificSettings = new ArrayMap<>(size);
+        for (int i = 0; i < size; ++i) {
+            String key = TextUtils.emptyIfNull(in.readString());
+            String val = TextUtils.emptyIfNull(in.readString());
+            if (key.trim().isEmpty()) {
+                continue;
+            }
+            deviceSpecificSettings.put(key, val);
+        }
+        mDeviceSpecificSettings = Collections.unmodifiableMap(deviceSpecificSettings);
+
+        mDisableAnimation = in.readBoolean();
+        mDisableAod = in.readBoolean();
+        mDisableLaunchBoost = in.readBoolean();
+        mDisableOptionalSensors = in.readBoolean();
+        mDisableVibration = in.readBoolean();
+        mEnableAdjustBrightness = in.readBoolean();
+        mEnableDataSaver = in.readBoolean();
+        mEnableFirewall = in.readBoolean();
+        mEnableNightMode = in.readBoolean();
+        mEnableQuickDoze = in.readBoolean();
+        mForceAllAppsStandby = in.readBoolean();
+        mForceBackgroundCheck = in.readBoolean();
+        mLocationMode = Math.max(PowerManager.MIN_LOCATION_MODE,
+                Math.min(in.readInt(), PowerManager.MAX_LOCATION_MODE));
+        mSoundTriggerMode = Math.max(PowerManager.MIN_SOUND_TRIGGER_MODE,
+                Math.min(in.readInt(), PowerManager.MAX_SOUND_TRIGGER_MODE));
+    }
+
+    public static final @android.annotation.NonNull Creator<BatterySaverPolicyConfig> CREATOR =
+            new Creator<BatterySaverPolicyConfig>() {
+                @Override
+                public BatterySaverPolicyConfig createFromParcel(Parcel in) {
+                    return new BatterySaverPolicyConfig(in);
+                }
+
+                @Override
+                public BatterySaverPolicyConfig[] newArray(int size) {
+                    return new BatterySaverPolicyConfig[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(mAdjustBrightnessFactor);
+        dest.writeBoolean(mAdvertiseIsEnabled);
+        dest.writeBoolean(mDeferFullBackup);
+        dest.writeBoolean(mDeferKeyValueBackup);
+
+        final Set<Map.Entry<String, String>> entries = mDeviceSpecificSettings.entrySet();
+        final int size = entries.size();
+        dest.writeInt(size);
+        for (Map.Entry<String, String> entry : entries) {
+            dest.writeString(entry.getKey());
+            dest.writeString(entry.getValue());
+        }
+
+        dest.writeBoolean(mDisableAnimation);
+        dest.writeBoolean(mDisableAod);
+        dest.writeBoolean(mDisableLaunchBoost);
+        dest.writeBoolean(mDisableOptionalSensors);
+        dest.writeBoolean(mDisableVibration);
+        dest.writeBoolean(mEnableAdjustBrightness);
+        dest.writeBoolean(mEnableDataSaver);
+        dest.writeBoolean(mEnableFirewall);
+        dest.writeBoolean(mEnableNightMode);
+        dest.writeBoolean(mEnableQuickDoze);
+        dest.writeBoolean(mForceAllAppsStandby);
+        dest.writeBoolean(mForceBackgroundCheck);
+        dest.writeInt(mLocationMode);
+        dest.writeInt(mSoundTriggerMode);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (Map.Entry<String, String> entry : mDeviceSpecificSettings.entrySet()) {
+            sb.append(entry.getKey()).append("=").append(entry.getValue()).append(",");
+        }
+        return "adjust_brightness_disabled=" + !mEnableAdjustBrightness + ","
+                + "adjust_brightness_factor=" + mAdjustBrightnessFactor + ","
+                + "advertise_is_enabled=" + mAdvertiseIsEnabled + ","
+                + "animation_disabled=" + mDisableAnimation + ","
+                + "aod_disabled=" + mDisableAod + ","
+                + "datasaver_disabled=" + !mEnableDataSaver + ","
+                + "enable_night_mode=" + mEnableNightMode + ","
+                + "firewall_disabled=" + !mEnableFirewall + ","
+                + "force_all_apps_standby=" + mForceAllAppsStandby + ","
+                + "force_background_check=" + mForceBackgroundCheck + ","
+                + "fullbackup_deferred=" + mDeferFullBackup + ","
+                + "gps_mode=" + mLocationMode + ","
+                + "keyvaluebackup_deferred=" + mDeferKeyValueBackup + ","
+                + "launch_boost_disabled=" + mDisableLaunchBoost + ","
+                + "optional_sensors_disabled=" + mDisableOptionalSensors + ","
+                + "quick_doze_enabled=" + mEnableQuickDoze + ","
+                + "soundtrigger_mode=" + mSoundTriggerMode + ","
+                + "vibration_disabled=" + mDisableVibration + ","
+                + sb.toString();
+    }
+
+    /**
+     * How much to adjust the screen brightness while in Battery Saver. This will have no effect
+     * if {@link #getEnableAdjustBrightness()} is {@code false}.
+     */
+    public float getAdjustBrightnessFactor() {
+        return mAdjustBrightnessFactor;
+    }
+
+    /**
+     * Whether or not to tell the system (and other apps) that Battery Saver is currently enabled.
+     */
+    public boolean getAdvertiseIsEnabled() {
+        return mAdvertiseIsEnabled;
+    }
+
+    /** Whether or not to defer full backup while in Battery Saver. */
+    public boolean getDeferFullBackup() {
+        return mDeferFullBackup;
+    }
+
+    /** Whether or not to defer key-value backup while in Battery Saver. */
+    public boolean getDeferKeyValueBackup() {
+        return mDeferKeyValueBackup;
+    }
+
+    /**
+     * Returns the device-specific battery saver constants.
+     */
+    @NonNull
+    public Map<String, String> getDeviceSpecificSettings() {
+        return mDeviceSpecificSettings;
+    }
+
+    /** Whether or not to disable animation while in Battery Saver. */
+    public boolean getDisableAnimation() {
+        return mDisableAnimation;
+    }
+
+    /** Whether or not to disable Always On Display while in Battery Saver. */
+    public boolean getDisableAod() {
+        return mDisableAod;
+    }
+
+    /** Whether or not to disable launch boost while in Battery Saver. */
+    public boolean getDisableLaunchBoost() {
+        return mDisableLaunchBoost;
+    }
+
+    /** Whether or not to disable optional sensors while in Battery Saver. */
+    public boolean getDisableOptionalSensors() {
+        return mDisableOptionalSensors;
+    }
+
+    /**
+     * Get the SoundTrigger mode while in Battery Saver.
+     */
+    @PowerManager.SoundTriggerPowerSaveMode
+    public int getSoundTriggerMode() {
+        return mSoundTriggerMode;
+    }
+
+    /**
+     * Whether or not to disable {@link android.hardware.soundtrigger.SoundTrigger}
+     * while in Battery Saver.
+     * @deprecated Use {@link #getSoundTriggerMode()} instead.
+     */
+    @Deprecated
+    public boolean getDisableSoundTrigger() {
+        return mSoundTriggerMode == PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED;
+    }
+
+    /** Whether or not to disable vibration while in Battery Saver. */
+    public boolean getDisableVibration() {
+        return mDisableVibration;
+    }
+
+    /** Whether or not to enable brightness adjustment while in Battery Saver. */
+    public boolean getEnableAdjustBrightness() {
+        return mEnableAdjustBrightness;
+    }
+
+    /** Whether or not to enable Data Saver while in Battery Saver. */
+    public boolean getEnableDataSaver() {
+        return mEnableDataSaver;
+    }
+
+    /**
+     * Whether or not to enable network firewall rules to restrict background network use
+     * while in Battery Saver.
+     */
+    public boolean getEnableFirewall() {
+        return mEnableFirewall;
+    }
+
+    /** Whether or not to enable night mode while in Battery Saver. */
+    public boolean getEnableNightMode() {
+        return mEnableNightMode;
+    }
+
+    /** Whether or not to enable Quick Doze while in Battery Saver. */
+    public boolean getEnableQuickDoze() {
+        return mEnableQuickDoze;
+    }
+
+    /** Whether or not to force all apps to standby mode while in Battery Saver. */
+    public boolean getForceAllAppsStandby() {
+        return mForceAllAppsStandby;
+    }
+
+    /**
+     * Whether or not to force background check (disallow background services and manifest
+     * broadcast receivers) on all apps (not just apps targeting Android
+     * {@link Build.VERSION_CODES#O} and above)
+     * while in Battery Saver.
+     */
+    public boolean getForceBackgroundCheck() {
+        return mForceBackgroundCheck;
+    }
+
+    /** The location mode while in Battery Saver. */
+    public int getLocationMode() {
+        return mLocationMode;
+    }
+
+    /** Builder class for constructing {@link BatterySaverPolicyConfig} objects. */
+    public static final class Builder {
+        private float mAdjustBrightnessFactor = 1f;
+        private boolean mAdvertiseIsEnabled = false;
+        private boolean mDeferFullBackup = false;
+        private boolean mDeferKeyValueBackup = false;
+        @NonNull
+        private final ArrayMap<String, String> mDeviceSpecificSettings = new ArrayMap<>();
+        private boolean mDisableAnimation = false;
+        private boolean mDisableAod = false;
+        private boolean mDisableLaunchBoost = false;
+        private boolean mDisableOptionalSensors = false;
+        private boolean mDisableVibration = false;
+        private boolean mEnableAdjustBrightness = false;
+        private boolean mEnableDataSaver = false;
+        private boolean mEnableFirewall = false;
+        private boolean mEnableNightMode = false;
+        private boolean mEnableQuickDoze = false;
+        private boolean mForceAllAppsStandby = false;
+        private boolean mForceBackgroundCheck = false;
+        private int mLocationMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+        private int mSoundTriggerMode = PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED;
+
+        public Builder() {
+        }
+
+        /**
+         * Creates a Builder prepopulated with the values from the passed in
+         * {@link BatterySaverPolicyConfig}.
+         */
+        public Builder(@NonNull BatterySaverPolicyConfig batterySaverPolicyConfig) {
+            mAdjustBrightnessFactor = batterySaverPolicyConfig.getAdjustBrightnessFactor();
+            mAdvertiseIsEnabled = batterySaverPolicyConfig.getAdvertiseIsEnabled();
+            mDeferFullBackup = batterySaverPolicyConfig.getDeferFullBackup();
+            mDeferKeyValueBackup = batterySaverPolicyConfig.getDeferKeyValueBackup();
+
+            for (String key :
+                    batterySaverPolicyConfig.getDeviceSpecificSettings().keySet()) {
+                mDeviceSpecificSettings.put(key,
+                        batterySaverPolicyConfig.getDeviceSpecificSettings().get(key));
+            }
+
+            mDisableAnimation = batterySaverPolicyConfig.getDisableAnimation();
+            mDisableAod = batterySaverPolicyConfig.getDisableAod();
+            mDisableLaunchBoost = batterySaverPolicyConfig.getDisableLaunchBoost();
+            mDisableOptionalSensors = batterySaverPolicyConfig.getDisableOptionalSensors();
+            mDisableVibration = batterySaverPolicyConfig.getDisableVibration();
+            mEnableAdjustBrightness = batterySaverPolicyConfig.getEnableAdjustBrightness();
+            mEnableDataSaver = batterySaverPolicyConfig.getEnableDataSaver();
+            mEnableFirewall = batterySaverPolicyConfig.getEnableFirewall();
+            mEnableNightMode = batterySaverPolicyConfig.getEnableNightMode();
+            mEnableQuickDoze = batterySaverPolicyConfig.getEnableQuickDoze();
+            mForceAllAppsStandby = batterySaverPolicyConfig.getForceAllAppsStandby();
+            mForceBackgroundCheck = batterySaverPolicyConfig.getForceBackgroundCheck();
+            mLocationMode = batterySaverPolicyConfig.getLocationMode();
+            mSoundTriggerMode = batterySaverPolicyConfig.getSoundTriggerMode();
+        }
+
+        /**
+         * Set how much to adjust the screen brightness while in Battery Saver. The value should
+         * be in the [0, 1] range, where 1 will not change the brightness. This will have no
+         * effect if {@link #setEnableAdjustBrightness(boolean)} is not called with {@code true}.
+         */
+        @NonNull
+        public Builder setAdjustBrightnessFactor(float adjustBrightnessFactor) {
+            mAdjustBrightnessFactor = adjustBrightnessFactor;
+            return this;
+        }
+
+        /**
+         * Set whether or not to tell the system (and other apps) that Battery Saver is
+         * currently enabled.
+         */
+        @NonNull
+        public Builder setAdvertiseIsEnabled(boolean advertiseIsEnabled) {
+            mAdvertiseIsEnabled = advertiseIsEnabled;
+            return this;
+        }
+
+        /** Set whether or not to defer full backup while in Battery Saver. */
+        @NonNull
+        public Builder setDeferFullBackup(boolean deferFullBackup) {
+            mDeferFullBackup = deferFullBackup;
+            return this;
+        }
+
+        /** Set whether or not to defer key-value backup while in Battery Saver. */
+        @NonNull
+        public Builder setDeferKeyValueBackup(boolean deferKeyValueBackup) {
+            mDeferKeyValueBackup = deferKeyValueBackup;
+            return this;
+        }
+
+        /**
+         * Adds a key-value pair for device-specific battery saver constants. The supported keys
+         * and values are the same as those in
+         * {@link android.provider.Settings.Global#BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS}.
+         *
+         * @throws IllegalArgumentException if the provided key is invalid (empty, null, or all
+         * whitespace)
+         */
+        @NonNull
+        public Builder addDeviceSpecificSetting(@NonNull String key, @NonNull String value) {
+            if (key == null) {
+                throw new IllegalArgumentException("Key cannot be null");
+            }
+            key = key.trim();
+            if (TextUtils.isEmpty(key)) {
+                throw new IllegalArgumentException("Key cannot be empty");
+            }
+            mDeviceSpecificSettings.put(key, TextUtils.emptyIfNull(value));
+            return this;
+        }
+
+        /** Set whether or not to disable animation while in Battery Saver. */
+        @NonNull
+        public Builder setDisableAnimation(boolean disableAnimation) {
+            mDisableAnimation = disableAnimation;
+            return this;
+        }
+
+        /** Set whether or not to disable Always On Display while in Battery Saver. */
+        @NonNull
+        public Builder setDisableAod(boolean disableAod) {
+            mDisableAod = disableAod;
+            return this;
+        }
+
+        /** Set whether or not to disable launch boost while in Battery Saver. */
+        @NonNull
+        public Builder setDisableLaunchBoost(boolean disableLaunchBoost) {
+            mDisableLaunchBoost = disableLaunchBoost;
+            return this;
+        }
+
+        /** Set whether or not to disable optional sensors while in Battery Saver. */
+        @NonNull
+        public Builder setDisableOptionalSensors(boolean disableOptionalSensors) {
+            mDisableOptionalSensors = disableOptionalSensors;
+            return this;
+        }
+
+        /**
+         * Set whether or not to disable  {@link android.hardware.soundtrigger.SoundTrigger}
+         * while in Battery Saver.
+         * @deprecated Use {@link #setSoundTriggerMode(int)} instead.
+         */
+        @Deprecated
+        @NonNull
+        public Builder setDisableSoundTrigger(boolean disableSoundTrigger) {
+            if (disableSoundTrigger) {
+                mSoundTriggerMode = PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED;
+            } else {
+                mSoundTriggerMode = PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED;
+            }
+            return this;
+        }
+
+        /**
+         * Set the SoundTrigger mode while in Battery Saver.
+         */
+        @NonNull
+        public Builder setSoundTriggerMode(
+                @PowerManager.SoundTriggerPowerSaveMode int soundTriggerMode) {
+            mSoundTriggerMode = soundTriggerMode;
+            return this;
+        }
+
+        /** Set whether or not to disable vibration while in Battery Saver. */
+        @NonNull
+        public Builder setDisableVibration(boolean disableVibration) {
+            mDisableVibration = disableVibration;
+            return this;
+        }
+
+        /** Set whether or not to enable brightness adjustment while in Battery Saver. */
+        @NonNull
+        public Builder setEnableAdjustBrightness(boolean enableAdjustBrightness) {
+            mEnableAdjustBrightness = enableAdjustBrightness;
+            return this;
+        }
+
+        /** Set whether or not to enable Data Saver while in Battery Saver. */
+        @NonNull
+        public Builder setEnableDataSaver(boolean enableDataSaver) {
+            mEnableDataSaver = enableDataSaver;
+            return this;
+        }
+
+        /**
+         * Set whether or not to enable network firewall rules to restrict background network use
+         * while in Battery Saver.
+         */
+        @NonNull
+        public Builder setEnableFirewall(boolean enableFirewall) {
+            mEnableFirewall = enableFirewall;
+            return this;
+        }
+
+        /** Set whether or not to enable night mode while in Battery Saver. */
+        @NonNull
+        public Builder setEnableNightMode(boolean enableNightMode) {
+            mEnableNightMode = enableNightMode;
+            return this;
+        }
+
+        /** Set whether or not to enable Quick Doze while in Battery Saver. */
+        @NonNull
+        public Builder setEnableQuickDoze(boolean enableQuickDoze) {
+            mEnableQuickDoze = enableQuickDoze;
+            return this;
+        }
+
+        /** Set whether or not to force all apps to standby mode while in Battery Saver. */
+        @NonNull
+        public Builder setForceAllAppsStandby(boolean forceAllAppsStandby) {
+            mForceAllAppsStandby = forceAllAppsStandby;
+            return this;
+        }
+
+        /**
+         * Set whether or not to force background check (disallow background services and manifest
+         * broadcast receivers) on all apps (not just apps targeting Android
+         * {@link Build.VERSION_CODES#O} and above)
+         * while in Battery Saver.
+         */
+        @NonNull
+        public Builder setForceBackgroundCheck(boolean forceBackgroundCheck) {
+            mForceBackgroundCheck = forceBackgroundCheck;
+            return this;
+        }
+
+        /** Set the location mode while in Battery Saver. */
+        @NonNull
+        public Builder setLocationMode(@PowerManager.LocationPowerSaveMode int locationMode) {
+            mLocationMode = locationMode;
+            return this;
+        }
+
+        /**
+         * Build a {@link BatterySaverPolicyConfig} object using the set parameters. This object
+         * is immutable.
+         */
+        @NonNull
+        public BatterySaverPolicyConfig build() {
+            return new BatterySaverPolicyConfig(this);
+        }
+    }
+}
diff --git a/android-34/android/os/BatteryStats.java b/android-34/android/os/BatteryStats.java
new file mode 100644
index 0000000..2bad670
--- /dev/null
+++ b/android-34/android/os/BatteryStats.java
@@ -0,0 +1,9006 @@
+/*
+ * 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.
+ * 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.os;
+
+import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
+import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.job.JobParameters;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.location.GnssSignalQuality;
+import android.os.BatteryStatsManager.WifiState;
+import android.os.BatteryStatsManager.WifiSupplState;
+import android.server.ServerProtoEnums;
+import android.service.batterystats.BatteryStatsServiceDumpHistoryProto;
+import android.service.batterystats.BatteryStatsServiceDumpProto;
+import android.telephony.CellSignalStrength;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+import android.util.MutableBoolean;
+import android.util.Pair;
+import android.util.Printer;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseDoubleArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistoryIterator;
+
+import com.google.android.collect.Lists;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * A class providing access to battery usage statistics, including information on
+ * wakelocks, processes, packages, and services.  All times are represented in microseconds
+ * except where indicated otherwise.
+ * @hide
+ */
+public abstract class BatteryStats {
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public BatteryStats() {}
+
+    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 = Context.BATTERY_STATS_SERVICE;
+
+    /**
+     * A constant indicating a partial wake lock timer.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int WAKE_TYPE_PARTIAL = 0;
+
+    /**
+     * A constant indicating a full wake lock timer.
+     */
+    public static final int WAKE_TYPE_FULL = 1;
+
+    /**
+     * A constant indicating a window wake lock timer.
+     */
+    public static final int WAKE_TYPE_WINDOW = 2;
+
+    /**
+     * A constant indicating a sensor timer.
+     */
+    public static final int SENSOR = 3;
+
+    /**
+     * A constant indicating a a wifi running timer
+     */
+    public static final int WIFI_RUNNING = 4;
+
+    /**
+     * A constant indicating a full wifi lock timer
+     */
+    public static final int FULL_WIFI_LOCK = 5;
+
+    /**
+     * A constant indicating a wifi scan
+     */
+    public static final int WIFI_SCAN = 6;
+
+    /**
+     * A constant indicating a wifi multicast timer
+     */
+    public static final int WIFI_MULTICAST_ENABLED = 7;
+
+    /**
+     * A constant indicating a video turn on timer
+     */
+    public static final int VIDEO_TURNED_ON = 8;
+
+    /**
+     * A constant indicating a vibrator on timer
+     */
+    public static final int VIBRATOR_ON = 9;
+
+    /**
+     * A constant indicating a foreground activity timer
+     */
+    public static final int FOREGROUND_ACTIVITY = 10;
+
+    /**
+     * A constant indicating a wifi batched scan is active
+     */
+    public static final int WIFI_BATCHED_SCAN = 11;
+
+    /**
+     * A constant indicating a process state timer
+     */
+    public static final int PROCESS_STATE = 12;
+
+    /**
+     * A constant indicating a sync timer
+     */
+    public static final int SYNC = 13;
+
+    /**
+     * A constant indicating a job timer
+     */
+    public static final int JOB = 14;
+
+    /**
+     * A constant indicating an audio turn on timer
+     */
+    public static final int AUDIO_TURNED_ON = 15;
+
+    /**
+     * A constant indicating a flashlight turn on timer
+     */
+    public static final int FLASHLIGHT_TURNED_ON = 16;
+
+    /**
+     * A constant indicating a camera turn on timer
+     */
+    public static final int CAMERA_TURNED_ON = 17;
+
+    /**
+     * A constant indicating a draw wake lock timer.
+     */
+    public static final int WAKE_TYPE_DRAW = 18;
+
+    /**
+     * A constant indicating a bluetooth scan timer.
+     */
+    public static final int BLUETOOTH_SCAN_ON = 19;
+
+    /**
+     * A constant indicating an aggregated partial wake lock timer.
+     */
+    public static final int AGGREGATED_WAKE_TYPE_PARTIAL = 20;
+
+    /**
+     * A constant indicating a bluetooth scan timer for unoptimized scans.
+     */
+    public static final int BLUETOOTH_UNOPTIMIZED_SCAN_ON = 21;
+
+    /**
+     * A constant indicating a foreground service timer
+     */
+    public static final int FOREGROUND_SERVICE = 22;
+
+    /**
+     * A constant indicating an aggregate wifi multicast timer
+     */
+     public static final int WIFI_AGGREGATE_MULTICAST_ENABLED = 23;
+
+    /**
+     * Include all of the data in the stats, including previously saved data.
+     */
+    public static final int STATS_SINCE_CHARGED = 0;
+
+    /**
+     * Include only the current run in the stats.
+     *
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, only {@link #STATS_SINCE_CHARGED}
+     * is supported.
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public static final int STATS_CURRENT = 1;
+
+    /**
+     * Include only the run since the last time the device was unplugged in the stats.
+     *
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, only {@link #STATS_SINCE_CHARGED}
+     * is supported.
+     */
+    @Deprecated
+    public static final int STATS_SINCE_UNPLUGGED = 2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "STATS_" }, value = {
+            STATS_SINCE_CHARGED,
+            STATS_CURRENT,
+            STATS_SINCE_UNPLUGGED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatName {}
+
+    // NOTE: Update this list if you add/change any stats above.
+    // These characters are supposed to represent "total", "last", "current",
+    // and "unplugged". They were shortened for efficiency sake.
+    private static final String[] STAT_NAMES = { "l", "c", "u" };
+
+    /**
+     * Current version of checkin data format.
+     *
+     * New in version 19:
+     *   - Wakelock data (wl) gets current and max times.
+     * New in version 20:
+     *   - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs, Syncs.
+     * New in version 21:
+     *   - Actual (not just apportioned) Wakelock time is also recorded.
+     *   - Aggregated partial wakelock time (per uid, instead of per wakelock) is recorded.
+     *   - BLE scan result count
+     *   - CPU frequency time per uid
+     * New in version 22:
+     *   - BLE scan result background count, BLE unoptimized scan time
+     *   - Background partial wakelock time & count
+     * New in version 23:
+     *   - Logging smeared power model values
+     * New in version 24:
+     *   - 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]
+     * New in version 27:
+     *   - Always On Display (screen doze mode) time and power
+     * New in version 28:
+     *   - Light/Deep Doze power
+     *   - WiFi Multicast Wakelock statistics (count & duration)
+     * New in version 29:
+     *   - Process states re-ordered. TOP_SLEEPING now below BACKGROUND. HEAVY_WEIGHT introduced.
+     *   - CPU times per UID process state
+     * New in version 30:
+     *   - Uid.PROCESS_STATE_FOREGROUND_SERVICE only tracks
+     *   ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE.
+     * New in version 31:
+     *   - New cellular network types.
+     *   - Deferred job metrics.
+     * New in version 32:
+     *   - Ambient display properly output in data dump.
+     * New in version 33:
+     *   - Fixed bug in min learned capacity updating process.
+     * New in version 34:
+     *   - Deprecated STATS_SINCE_UNPLUGGED and STATS_CURRENT.
+     * New in version 35:
+     *   - Fixed bug that was not reporting high cellular tx power correctly
+     *   - Added out of service and emergency service modes to data connection types
+     * New in version 36:
+     *   - Added PowerStats and CPU time-in-state data
+     */
+    static final int CHECKIN_VERSION = 36;
+
+    /**
+     * Old version, we hit 9 and ran out of room, need to remove.
+     */
+    private static final int BATTERY_STATS_CHECKIN_VERSION = 9;
+
+    private static final long BYTES_PER_KB = 1024;
+    private static final long BYTES_PER_MB = 1048576; // 1024^2
+    private static final long BYTES_PER_GB = 1073741824; //1024^3
+    public static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
+
+    private static final String VERSION_DATA = "vers";
+    private static final String UID_DATA = "uid";
+    private static final String WAKEUP_ALARM_DATA = "wua";
+    private static final String APK_DATA = "apk";
+    private static final String PROCESS_DATA = "pr";
+    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";
+    // fgs line is:
+    // BATTERY_STATS_CHECKIN_VERSION, uid, category, "fgs",
+    // foreground service time, count
+    private static final String FOREGROUND_SERVICE_DATA = "fgs";
+    private static final String STATE_TIME_DATA = "st";
+    // wl line is:
+    // BATTERY_STATS_CHECKIN_VERSION, uid, which, "wl", name,
+    // full        totalTime, 'f',  count, current duration, max duration, total duration,
+    // partial     totalTime, 'p',  count, current duration, max duration, total duration,
+    // bg partial  totalTime, 'bp', count, current duration, max duration, total duration,
+    // window      totalTime, 'w',  count, current duration, max duration, total duration
+    // [Currently, full and window wakelocks have durations current = max = total = -1]
+    private static final String WAKELOCK_DATA = "wl";
+    // awl line is:
+    // BATTERY_STATS_CHECKIN_VERSION, uid, which, "awl",
+    // cumulative partial wakelock duration, cumulative background partial wakelock duration
+    private static final String AGGREGATED_WAKELOCK_DATA = "awl";
+    private static final String SYNC_DATA = "sy";
+    private static final String JOB_DATA = "jb";
+    private static final String JOB_COMPLETION_DATA = "jbc";
+
+    /**
+     * jbd line is:
+     * BATTERY_STATS_CHECKIN_VERSION, uid, which, "jbd",
+     * jobsDeferredEventCount, jobsDeferredCount, totalLatencyMillis,
+     * count at latency < 1 hr, count at latency 1 to 2 hrs, 2 to 4 hrs, 4 to 8 hrs, and past 8 hrs
+     * <p>
+     * @see #JOB_FRESHNESS_BUCKETS
+     */
+    private static final String JOBS_DEFERRED_DATA = "jbd";
+    private static final String KERNEL_WAKELOCK_DATA = "kwl";
+    private static final String WAKEUP_REASON_DATA = "wr";
+    private static final String NETWORK_DATA = "nt";
+    private static final String USER_ACTIVITY_DATA = "ua";
+    private static final String BATTERY_DATA = "bt";
+    private static final String BATTERY_DISCHARGE_DATA = "dc";
+    private static final String BATTERY_LEVEL_DATA = "lv";
+    private static final String GLOBAL_WIFI_DATA = "gwfl";
+    private static final String WIFI_DATA = "wfl";
+    private static final String GLOBAL_WIFI_CONTROLLER_DATA = "gwfcd";
+    private static final String WIFI_CONTROLLER_DATA = "wfcd";
+    private static final String GLOBAL_BLUETOOTH_CONTROLLER_DATA = "gble";
+    private static final String BLUETOOTH_CONTROLLER_DATA = "ble";
+    private static final String BLUETOOTH_MISC_DATA = "blem";
+    private static final String MISC_DATA = "m";
+    private static final String GLOBAL_NETWORK_DATA = "gn";
+    private static final String GLOBAL_MODEM_CONTROLLER_DATA = "gmcd";
+    private static final String MODEM_CONTROLLER_DATA = "mcd";
+    private static final String HISTORY_STRING_POOL = "hsp";
+    private static final String HISTORY_DATA = "h";
+    private static final String SCREEN_BRIGHTNESS_DATA = "br";
+    private static final String SIGNAL_STRENGTH_TIME_DATA = "sgt";
+    private static final String SIGNAL_SCANNING_TIME_DATA = "sst";
+    private static final String SIGNAL_STRENGTH_COUNT_DATA = "sgc";
+    private static final String DATA_CONNECTION_TIME_DATA = "dct";
+    private static final String DATA_CONNECTION_COUNT_DATA = "dcc";
+    private static final String WIFI_STATE_TIME_DATA = "wst";
+    private static final String WIFI_STATE_COUNT_DATA = "wsc";
+    private static final String WIFI_SUPPL_STATE_TIME_DATA = "wsst";
+    private static final String WIFI_SUPPL_STATE_COUNT_DATA = "wssc";
+    private static final String WIFI_SIGNAL_STRENGTH_TIME_DATA = "wsgt";
+    private static final String WIFI_SIGNAL_STRENGTH_COUNT_DATA = "wsgc";
+    private static final String POWER_USE_SUMMARY_DATA = "pws";
+    private static final String POWER_USE_ITEM_DATA = "pwi";
+    private static final String DISCHARGE_STEP_DATA = "dsd";
+    private static final String CHARGE_STEP_DATA = "csd";
+    private static final String DISCHARGE_TIME_REMAIN_DATA = "dtr";
+    private static final String CHARGE_TIME_REMAIN_DATA = "ctr";
+    private static final String FLASHLIGHT_DATA = "fla";
+    private static final String CAMERA_DATA = "cam";
+    private static final String VIDEO_DATA = "vid";
+    private static final String AUDIO_DATA = "aud";
+    private static final String WIFI_MULTICAST_TOTAL_DATA = "wmct";
+    private static final String WIFI_MULTICAST_DATA = "wmc";
+
+    public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
+
+    private final StringBuilder mFormatBuilder = new StringBuilder(32);
+    private final Formatter mFormatter = new Formatter(mFormatBuilder);
+
+    private static final String CELLULAR_CONTROLLER_NAME = "Cellular";
+    private static final String WIFI_CONTROLLER_NAME = "WiFi";
+
+    /**
+     * Indicates times spent by the uid at each cpu frequency in all process states.
+     *
+     * Other types might include times spent in foreground, background etc.
+     */
+    @VisibleForTesting
+    public static final String UID_TIMES_TYPE_ALL = "A";
+
+    /**
+     * These are the thresholds for bucketing last time since a job was run for an app
+     * that just moved to ACTIVE due to a launch. So if the last time a job ran was less
+     * than 1 hour ago, then it's reasonably fresh, 2 hours ago, not so fresh and so
+     * on.
+     */
+    public static final long[] JOB_FRESHNESS_BUCKETS = {
+            1 * 60 * 60 * 1000L,
+            2 * 60 * 60 * 1000L,
+            4 * 60 * 60 * 1000L,
+            8 * 60 * 60 * 1000L,
+            Long.MAX_VALUE
+    };
+
+    /**
+     * State for keeping track of counting information.
+     */
+    public static abstract class Counter {
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+        public Counter() {}
+
+        /**
+         * Returns the count associated with this Counter for the
+         * selected type of statistics.
+         *
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+         */
+        @UnsupportedAppUsage
+        public abstract int getCountLocked(int which);
+
+        /**
+         * Temporary for debugging.
+         */
+        public abstract void logState(Printer pw, String prefix);
+    }
+
+    /**
+     * State for keeping track of long counting information.
+     */
+    public static abstract class LongCounter {
+
+        /**
+         * Returns the count associated with this Counter for the
+         * selected type of statistics.
+         *
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+         */
+        public abstract long getCountLocked(int which);
+
+        /**
+         * Returns the count accumulated by this Counter for the specified process state.
+         * If the counter does not support per-procstate tracking, returns 0.
+         */
+        public abstract long getCountForProcessState(@BatteryConsumer.ProcessState int procState);
+
+        /**
+         * Temporary for debugging.
+         */
+        public abstract void logState(Printer pw, String prefix);
+    }
+
+    /**
+     * State for keeping track of array of long counting information.
+     */
+    public static abstract class LongCounterArray {
+        /**
+         * Returns the counts associated with this Counter for the
+         * selected type of statistics.
+         *
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+         */
+        public abstract long[] getCountsLocked(int which);
+
+        /**
+         * Temporary for debugging.
+         */
+        public abstract void logState(Printer pw, String prefix);
+    }
+
+    /**
+     * Container class that aggregates counters for transmit, receive, and idle state of a
+     * radio controller.
+     */
+    public static abstract class ControllerActivityCounter {
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * idle state.
+         */
+        public abstract LongCounter getIdleTimeCounter();
+
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * scan state.
+         */
+        public abstract LongCounter getScanTimeCounter();
+
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * sleep state.
+         */
+        public abstract LongCounter getSleepTimeCounter();
+
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * receive state.
+         */
+        public abstract LongCounter getRxTimeCounter();
+
+        /**
+         * An array of {@link LongCounter}, representing various transmit levels, where each level
+         * may draw a different amount of power. The levels themselves are controller-specific.
+         * @return non-null array of {@link LongCounter}s representing time spent (milliseconds) in
+         * various transmit level states.
+         */
+        public abstract LongCounter[] getTxTimeCounters();
+
+        /**
+         * @return a non-null {@link LongCounter} representing the power consumed by the controller
+         * in all states, measured in milli-ampere-milliseconds (mAms). The counter may always
+         * yield a value of 0 if the device doesn't support power calculations.
+         */
+        public abstract LongCounter getPowerCounter();
+
+        /**
+         * @return a non-null {@link LongCounter} representing total power monitored on the rails
+         * in mAms (miliamps-milliseconds). The counter may always yield a value of 0 if the device
+         * doesn't support power rail monitoring.
+         */
+        public abstract LongCounter getMonitoredRailChargeConsumedMaMs();
+    }
+
+    /**
+     * State for keeping track of timing information.
+     */
+    public static abstract class Timer {
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+        public Timer() {}
+
+        /**
+         * Returns the count associated with this Timer for the
+         * selected type of statistics.
+         *
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+         */
+        @UnsupportedAppUsage
+        public abstract int getCountLocked(int which);
+
+        /**
+         * Returns the total time in microseconds associated with this Timer for the
+         * selected type of statistics.
+         *
+         * @param elapsedRealtimeUs current elapsed realtime of system in microseconds
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+         * @return a time in microseconds
+         */
+        @UnsupportedAppUsage
+        public abstract long getTotalTimeLocked(long elapsedRealtimeUs, int which);
+
+        /**
+         * Returns the total time in microseconds associated with this Timer since the
+         * 'mark' was last set.
+         *
+         * @param elapsedRealtimeUs current elapsed realtime of system in microseconds
+         * @return a time in microseconds
+         */
+        public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs);
+
+        /**
+         * Returns the max duration if it is being tracked.
+         * Not all Timer subclasses track the max, total, and current durations.
+         */
+        public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
+            return -1;
+        }
+
+        /**
+         * Returns the current time the timer has been active, if it is being tracked.
+         * Not all Timer subclasses track the max, total, and current durations.
+         */
+        public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
+            return -1;
+        }
+
+        /**
+         * Returns the total time the timer has been active, if it is being tracked.
+         *
+         * Returns the total cumulative duration (i.e. sum of past durations) that this timer has
+         * been on since reset.
+         * This may differ from getTotalTimeLocked(elapsedRealtimeUs, STATS_SINCE_CHARGED)/1000 since,
+         * depending on the Timer, getTotalTimeLocked may represent the total 'blamed' or 'pooled'
+         * time, rather than the actual time. By contrast, getTotalDurationMsLocked always gives
+         * the actual total time.
+         * Not all Timer subclasses track the max, total, and current durations.
+         */
+        public long getTotalDurationMsLocked(long elapsedRealtimeMs) {
+            return -1;
+        }
+
+        /**
+         * Returns the secondary Timer held by the Timer, if one exists. This secondary timer may be
+         * used, for example, for tracking background usage. Secondary timers are never pooled.
+         *
+         * Not all Timer subclasses have a secondary timer; those that don't return null.
+         */
+        public Timer getSubTimer() {
+            return null;
+        }
+
+        /**
+         * Returns whether the timer is currently running.  Some types of timers
+         * (e.g. BatchTimers) don't know whether the event is currently active,
+         * and report false.
+         */
+        public boolean isRunningLocked() {
+            return false;
+        }
+
+        /**
+         * Temporary for debugging.
+         */
+        public abstract void logState(Printer pw, String prefix);
+    }
+
+    /**
+     * Maps the ActivityManager procstate into corresponding BatteryStats procstate.
+     */
+    public static int mapToInternalProcessState(int procState) {
+        if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+            return Uid.PROCESS_STATE_NONEXISTENT;
+        } else if (procState == ActivityManager.PROCESS_STATE_TOP) {
+            return Uid.PROCESS_STATE_TOP;
+        } else if (procState == ActivityManager.PROCESS_STATE_BOUND_TOP) {
+            return Uid.PROCESS_STATE_BACKGROUND;
+        } else if (procState == ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            return Uid.PROCESS_STATE_FOREGROUND_SERVICE;
+        } else if (procState == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+            return Uid.PROCESS_STATE_FOREGROUND_SERVICE;
+        } else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+            // Persistent and other foreground states go here.
+            return Uid.PROCESS_STATE_FOREGROUND;
+        } else if (procState <= ActivityManager.PROCESS_STATE_RECEIVER) {
+            return Uid.PROCESS_STATE_BACKGROUND;
+        } else if (procState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+            return Uid.PROCESS_STATE_TOP_SLEEPING;
+        } else if (procState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+            return Uid.PROCESS_STATE_HEAVY_WEIGHT;
+        } else {
+            return Uid.PROCESS_STATE_CACHED;
+        }
+    }
+
+    /**
+     * Maps BatteryStats.Uid process state to the BatteryConsumer process state.
+     */
+    public static @BatteryConsumer.ProcessState int
+            mapUidProcessStateToBatteryConsumerProcessState(int processState) {
+        switch (processState) {
+            case BatteryStats.Uid.PROCESS_STATE_TOP:
+            case BatteryStats.Uid.PROCESS_STATE_FOREGROUND:
+                return BatteryConsumer.PROCESS_STATE_FOREGROUND;
+            case BatteryStats.Uid.PROCESS_STATE_BACKGROUND:
+            case BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING:
+                return BatteryConsumer.PROCESS_STATE_BACKGROUND;
+            case BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE:
+                return BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+            case BatteryStats.Uid.PROCESS_STATE_CACHED:
+                return BatteryConsumer.PROCESS_STATE_CACHED;
+            default:
+                return BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
+        }
+    }
+
+    /**
+     * Returns true if battery consumption is tracked on a per-process-state basis.
+     */
+    public abstract boolean isProcessStateDataAvailable();
+
+    /**
+     * The statistics associated with a particular uid.
+     */
+    public static abstract class Uid {
+
+        @UnsupportedAppUsage
+        public Uid() {
+        }
+
+        /**
+         * Returns a mapping containing wakelock statistics.
+         *
+         * @return a Map from Strings to Uid.Wakelock objects.
+         */
+        @UnsupportedAppUsage
+        public abstract ArrayMap<String, ? extends Wakelock> getWakelockStats();
+
+        /**
+         * Returns the WiFi Multicast Wakelock statistics.
+         *
+         * @return a Timer Object for the per uid Multicast statistics.
+         */
+        public abstract Timer getMulticastWakelockStats();
+
+        /**
+         * Returns a mapping containing sync statistics.
+         *
+         * @return a Map from Strings to Timer objects.
+         */
+        public abstract ArrayMap<String, ? extends Timer> getSyncStats();
+
+        /**
+         * Returns a mapping containing scheduled job statistics.
+         *
+         * @return a Map from Strings to Timer objects.
+         */
+        public abstract ArrayMap<String, ? extends Timer> getJobStats();
+
+        /**
+         * Returns statistics about how jobs have completed.
+         *
+         * @return A Map of String job names to completion type -> count mapping.
+         */
+        public abstract ArrayMap<String, SparseIntArray> getJobCompletionStats();
+
+        /**
+         * The statistics associated with a particular wake lock.
+         */
+        public static abstract class Wakelock {
+            @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+            public Wakelock() {}
+
+            @UnsupportedAppUsage
+            public abstract Timer getWakeTime(int type);
+        }
+
+        /**
+         * The cumulative time the uid spent holding any partial wakelocks. This will generally
+         * differ from summing over the Wakelocks in getWakelockStats since the latter may have
+         * wakelocks that overlap in time (and therefore over-counts).
+         */
+        public abstract Timer getAggregatedPartialWakelockTimer();
+
+        /**
+         * Returns a mapping containing sensor statistics.
+         *
+         * @return a Map from Integer sensor ids to Uid.Sensor objects.
+         */
+        @UnsupportedAppUsage
+        public abstract SparseArray<? extends Sensor> getSensorStats();
+
+        /**
+         * Returns a mapping containing active process data.
+         */
+        public abstract SparseArray<? extends Pid> getPidStats();
+
+        /**
+         * Returns a mapping containing process statistics.
+         *
+         * @return a Map from Strings to Uid.Proc objects.
+         */
+        @UnsupportedAppUsage
+        public abstract ArrayMap<String, ? extends Proc> getProcessStats();
+
+        /**
+         * Returns a mapping containing package statistics.
+         *
+         * @return a Map from Strings to Uid.Pkg objects.
+         */
+        @UnsupportedAppUsage
+        public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
+
+        /**
+         * Returns the proportion of power consumed by the System Service
+         * calls made by this UID.
+         */
+        public abstract double getProportionalSystemServiceUsage();
+
+        public abstract ControllerActivityCounter getWifiControllerActivity();
+        public abstract ControllerActivityCounter getBluetoothControllerActivity();
+        public abstract ControllerActivityCounter getModemControllerActivity();
+
+        /**
+         * {@hide}
+         */
+        @UnsupportedAppUsage
+        public abstract int getUid();
+
+        public abstract void noteWifiRunningLocked(long elapsedRealtime);
+        public abstract void noteWifiStoppedLocked(long elapsedRealtime);
+        public abstract void noteFullWifiLockAcquiredLocked(long elapsedRealtime);
+        public abstract void noteFullWifiLockReleasedLocked(long elapsedRealtime);
+        public abstract void noteWifiScanStartedLocked(long elapsedRealtime);
+        public abstract void noteWifiScanStoppedLocked(long elapsedRealtime);
+        public abstract void noteWifiBatchedScanStartedLocked(int csph, long elapsedRealtime);
+        public abstract void noteWifiBatchedScanStoppedLocked(long elapsedRealtime);
+        public abstract void noteWifiMulticastEnabledLocked(long elapsedRealtime);
+        public abstract void noteWifiMulticastDisabledLocked(long elapsedRealtime);
+        public abstract void noteActivityResumedLocked(long elapsedRealtime);
+        public abstract void noteActivityPausedLocked(long elapsedRealtime);
+        @UnsupportedAppUsage
+        public abstract long getWifiRunningTime(long elapsedRealtimeUs, int which);
+        @UnsupportedAppUsage
+        public abstract long getFullWifiLockTime(long elapsedRealtimeUs, int which);
+        @UnsupportedAppUsage
+        public abstract long getWifiScanTime(long elapsedRealtimeUs, int which);
+        public abstract int getWifiScanCount(int which);
+        /**
+         * Returns the timer keeping track of wifi scans.
+         */
+        public abstract Timer getWifiScanTimer();
+        public abstract int getWifiScanBackgroundCount(int which);
+        public abstract long getWifiScanActualTime(long elapsedRealtimeUs);
+        public abstract long getWifiScanBackgroundTime(long elapsedRealtimeUs);
+        /**
+         * Returns the timer keeping track of background wifi scans.
+         */
+        public abstract Timer getWifiScanBackgroundTimer();
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public abstract long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which);
+        public abstract int getWifiBatchedScanCount(int csphBin, int which);
+        @UnsupportedAppUsage
+        public abstract long getWifiMulticastTime(long elapsedRealtimeUs, int which);
+        @UnsupportedAppUsage
+        public abstract Timer getAudioTurnedOnTimer();
+        @UnsupportedAppUsage
+        public abstract Timer getVideoTurnedOnTimer();
+        public abstract Timer getFlashlightTurnedOnTimer();
+        public abstract Timer getCameraTurnedOnTimer();
+        public abstract Timer getForegroundActivityTimer();
+
+        /**
+         * Returns the timer keeping track of Foreground Service time
+         */
+        public abstract Timer getForegroundServiceTimer();
+        public abstract Timer getBluetoothScanTimer();
+        public abstract Timer getBluetoothScanBackgroundTimer();
+        public abstract Timer getBluetoothUnoptimizedScanTimer();
+        public abstract Timer getBluetoothUnoptimizedScanBackgroundTimer();
+        public abstract Counter getBluetoothScanResultCounter();
+        public abstract Counter getBluetoothScanResultBgCounter();
+
+        public abstract long[] getCpuFreqTimes(int which);
+        public abstract long[] getScreenOffCpuFreqTimes(int which);
+        /**
+         * Returns cpu active time of an uid.
+         */
+        public abstract long getCpuActiveTime();
+
+        /**
+         * Returns cpu active time of a UID while in the specified process state.
+         */
+        public abstract long getCpuActiveTime(int procState);
+
+        /**
+         * Returns cpu times of an uid on each cluster
+         */
+        public abstract long[] getCpuClusterTimes();
+
+        /**
+         * Returns cpu times of an uid at a particular process state.
+         */
+        public abstract boolean getCpuFreqTimes(@NonNull long[] timesInFreqMs, int procState);
+
+        /**
+         * Returns cpu times of an uid while the screen if off at a particular process state.
+         */
+        public abstract boolean getScreenOffCpuFreqTimes(@NonNull long[] timesInFreqMs,
+                int procState);
+
+        // Note: the following times are disjoint.  They can be added together to find the
+        // total time a uid has had any processes running at all.
+
+        /**
+         * Time this uid has any processes in the top state.
+         */
+        public static final int PROCESS_STATE_TOP = 0;
+        /**
+         * Time this uid has any process with a started foreground service, but
+         * none in the "top" state.
+         */
+        public static final int PROCESS_STATE_FOREGROUND_SERVICE = 1;
+        /**
+         * Time this uid has any process in an active foreground state, but none in the
+         * "foreground service" or better state. Persistent and other foreground states go here.
+         */
+        public static final int PROCESS_STATE_FOREGROUND = 2;
+        /**
+         * Time this uid has any process in an active background state, but none in the
+         * "foreground" or better state.
+         */
+        public static final int PROCESS_STATE_BACKGROUND = 3;
+        /**
+         * Time this uid has any process that is top while the device is sleeping, but not
+         * active for any other reason.  We kind-of consider it a kind of cached process
+         * for execution restrictions.
+         */
+        public static final int PROCESS_STATE_TOP_SLEEPING = 4;
+        /**
+         * Time this uid has any process that is in the background but it has an activity
+         * marked as "can't save state".  This is essentially a cached process, though the
+         * system will try much harder than normal to avoid killing it.
+         */
+        public static final int PROCESS_STATE_HEAVY_WEIGHT = 5;
+        /**
+         * Time this uid has any processes that are sitting around cached, not in one of the
+         * other active states.
+         */
+        public static final int PROCESS_STATE_CACHED = 6;
+        /**
+         * Total number of process states we track.
+         */
+        public static final int NUM_PROCESS_STATE = 7;
+        /**
+         * State of the UID when it has no running processes.  It is intentionally out of
+         * bounds 0..NUM_PROCESS_STATE.
+         */
+        public static final int PROCESS_STATE_NONEXISTENT = NUM_PROCESS_STATE;
+
+        // Used in dump
+        static final String[] PROCESS_STATE_NAMES = {
+                "Top", "Fg Service", "Foreground", "Background", "Top Sleeping", "Heavy Weight",
+                "Cached"
+        };
+
+        // Used in checkin dump
+        @VisibleForTesting
+        public static final String[] UID_PROCESS_TYPES = {
+                "T",  // TOP
+                "FS", // FOREGROUND_SERVICE
+                "F",  // FOREGROUND
+                "B",  // BACKGROUND
+                "TS", // TOP_SLEEPING
+                "HW",  // HEAVY_WEIGHT
+                "C"   // CACHED
+        };
+
+        public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
+        public abstract Timer getProcessStateTimer(int state);
+
+        public abstract Timer getVibratorOnTimer();
+
+        public static final int NUM_WIFI_BATCHED_SCAN_BINS = 5;
+
+        /**
+         * Note that these must match the constants in android.os.PowerManager.
+         * Also, if the user activity types change, the BatteryStatsImpl.VERSION must
+         * also be bumped.
+         */
+        static final String[] USER_ACTIVITY_TYPES = {
+            "other", "button", "touch", "accessibility", "attention", "faceDown", "deviceState"
+        };
+
+        public static final int NUM_USER_ACTIVITY_TYPES = USER_ACTIVITY_TYPES.length;
+
+        public abstract void noteUserActivityLocked(int type);
+        public abstract boolean hasUserActivity();
+        public abstract int getUserActivityCount(int type, int which);
+
+        public abstract boolean hasNetworkActivity();
+        @UnsupportedAppUsage
+        public abstract long getNetworkActivityBytes(int type, int which);
+        public abstract long getNetworkActivityPackets(int type, int which);
+        @UnsupportedAppUsage
+        public abstract long getMobileRadioActiveTime(int which);
+
+        /**
+         * Returns the amount of time (in microseconds) this UID was in the specified processState.
+         */
+        public abstract long getMobileRadioActiveTimeInProcessState(
+                @BatteryConsumer.ProcessState int processState);
+
+        public abstract int getMobileRadioActiveCount(int which);
+
+        /**
+         * Get the total cpu time (in microseconds) this UID had processes executing in userspace.
+         */
+        public abstract long getUserCpuTimeUs(int which);
+
+        /**
+         * Get the total cpu time (in microseconds) this UID had processes executing kernel syscalls.
+         */
+        public abstract long getSystemCpuTimeUs(int which);
+
+        /**
+         * Returns the approximate cpu time (in microseconds) spent at a certain CPU speed for a
+         * given CPU cluster.
+         * @param cluster the index of the CPU cluster.
+         * @param step the index of the CPU speed. This is not the actual speed of the CPU.
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+         * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
+         * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
+         */
+        public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);
+
+        /**
+         * Returns the number of times this UID woke up the Application Processor to
+         * process a mobile radio packet.
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+         */
+        public abstract long getMobileRadioApWakeupCount(int which);
+
+        /**
+         * Returns the number of times this UID woke up the Application Processor to
+         * process a WiFi packet.
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+         */
+        public abstract long getWifiRadioApWakeupCount(int which);
+
+        /**
+         * Appends the deferred jobs data to the StringBuilder passed in, in checkin format
+         * @param sb StringBuilder that can be overwritten with the deferred jobs data
+         * @param which one of STATS_*
+         */
+        public abstract void getDeferredJobsCheckinLineLocked(StringBuilder sb, int which);
+
+        /**
+         * Appends the deferred jobs data to the StringBuilder passed in
+         * @param sb StringBuilder that can be overwritten with the deferred jobs data
+         * @param which one of STATS_*
+         */
+        public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which);
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of bluetooth for this uid,
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#BLUETOOTH} bucket
+         * provided by the PowerStats service.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getBluetoothEnergyConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's bluetooth usage
+         * when in the specified process state.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getBluetoothEnergyConsumptionUC(
+                @BatteryConsumer.ProcessState int processState);
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#CPU} bucket
+         * provided by the PowerStats service.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getCpuEnergyConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's cpu usage when in the
+         * specified process state.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getCpuEnergyConsumptionUC(
+                @BatteryConsumer.ProcessState int processState);
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's GNSS usage, derived from
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#GNSS} bucket
+         * provided by the PowerStats service.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getGnssEnergyConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's radio usage, derived from
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#MOBILE_RADIO}
+         * bucket provided by the PowerStats service.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getMobileRadioEnergyConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's radio usage when in the
+         * specified process state.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getMobileRadioEnergyConsumptionUC(
+                @BatteryConsumer.ProcessState int processState);
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#DISPLAY} bucket
+         * provided by the PowerStats service.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getScreenOnEnergyConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of wifi for this uid,
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#WIFI} bucket
+         * provided by the PowerStats service.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getWifiEnergyConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of the uid's wifi usage when in the
+         * specified process state.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getWifiEnergyConsumptionUC(
+                @BatteryConsumer.ProcessState int processState);
+
+
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of UID's camera usage, derived from
+         * on-device power measurement data.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getCameraEnergyConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) used by this uid for each
+         * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
+         * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+         *
+         * @return charge (in microcoulombs) consumed since last reset for each (custom) energy
+         *         consumer of type OTHER, indexed by their ordinal. Returns null if no energy
+         *         reporting is supported.
+         *
+         * {@hide}
+         */
+        public abstract @Nullable long[] getCustomEnergyConsumerBatteryConsumptionUC();
+
+        public static abstract class Sensor {
+
+            @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+            public Sensor() {}
+
+            /*
+             * FIXME: it's not correct to use this magic value because it
+             * could clash with a sensor handle (which are defined by
+             * the sensor HAL, and therefore out of our control
+             */
+            // Magic sensor number for the GPS.
+            @UnsupportedAppUsage
+            public static final int GPS = -10000;
+
+            @UnsupportedAppUsage
+            public abstract int getHandle();
+
+            @UnsupportedAppUsage
+            public abstract Timer getSensorTime();
+
+            /** Returns a Timer for sensor usage when app is in the background. */
+            public abstract Timer getSensorBackgroundTime();
+        }
+
+        public class Pid {
+            public int mWakeNesting;
+            public long mWakeSumMs;
+            public long mWakeStartMs;
+        }
+
+        /**
+         * The statistics associated with a particular process.
+         */
+        public static abstract class Proc {
+
+            @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+            public Proc() {}
+
+            public static class ExcessivePower {
+
+                @UnsupportedAppUsage
+                public ExcessivePower() {
+                }
+
+                public static final int TYPE_WAKE = 1;
+                public static final int TYPE_CPU = 2;
+
+                @UnsupportedAppUsage
+                public int type;
+                @UnsupportedAppUsage
+                public long overTime;
+                @UnsupportedAppUsage
+                public long usedTime;
+            }
+
+            /**
+             * Returns true if this process is still active in the battery stats.
+             */
+            public abstract boolean isActive();
+
+            /**
+             * Returns the total time (in milliseconds) spent executing in user code.
+             *
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+             */
+            @UnsupportedAppUsage
+            public abstract long getUserTime(int which);
+
+            /**
+             * Returns the total time (in milliseconds) spent executing in system code.
+             *
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+             */
+            @UnsupportedAppUsage
+            public abstract long getSystemTime(int which);
+
+            /**
+             * Returns the number of times the process has been started.
+             *
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+             */
+            @UnsupportedAppUsage
+            public abstract int getStarts(int which);
+
+            /**
+             * Returns the number of times the process has crashed.
+             *
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+             */
+            public abstract int getNumCrashes(int which);
+
+            /**
+             * Returns the number of times the process has ANRed.
+             *
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+             */
+            public abstract int getNumAnrs(int which);
+
+            /**
+             * Returns the cpu time (milliseconds) spent while the process was in the foreground.
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+             * @return foreground cpu time in microseconds
+             */
+            @UnsupportedAppUsage
+            public abstract long getForegroundTime(int which);
+
+            @UnsupportedAppUsage
+            public abstract int countExcessivePowers();
+
+            @UnsupportedAppUsage
+            public abstract ExcessivePower getExcessivePower(int i);
+        }
+
+        /**
+         * The statistics associated with a particular package.
+         */
+        public static abstract class Pkg {
+
+            @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+            public Pkg() {}
+
+            /**
+             * Returns information about all wakeup alarms that have been triggered for this
+             * package.  The mapping keys are tag names for the alarms, the counter contains
+             * the number of times the alarm was triggered while on battery.
+             */
+            @UnsupportedAppUsage
+            public abstract ArrayMap<String, ? extends Counter> getWakeupAlarmStats();
+
+            /**
+             * Returns a mapping containing service statistics.
+             */
+            @UnsupportedAppUsage
+            public abstract ArrayMap<String, ? extends Serv> getServiceStats();
+
+            /**
+             * The statistics associated with a particular service.
+             */
+            public static abstract class Serv {
+
+                /**
+                 * Returns the amount of time spent started.
+                 *
+                 * @param batteryUptime elapsed uptime on battery in microseconds.
+                 * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+                 * @return
+                 */
+                @UnsupportedAppUsage
+                public abstract long getStartTime(long batteryUptime, int which);
+
+                /**
+                 * Returns the total number of times startService() has been called.
+                 *
+                 * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+                 */
+                @UnsupportedAppUsage
+                public abstract int getStarts(int which);
+
+                /**
+                 * Returns the total number times the service has been launched.
+                 *
+                 * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+                 */
+                @UnsupportedAppUsage
+                public abstract int getLaunches(int which);
+            }
+        }
+    }
+
+    public static final class LevelStepTracker {
+        public long mLastStepTime = -1;
+        public int mNumStepDurations;
+        public final long[] mStepDurations;
+
+        public LevelStepTracker(int maxLevelSteps) {
+            mStepDurations = new long[maxLevelSteps];
+        }
+
+        public LevelStepTracker(int numSteps, long[] steps) {
+            mNumStepDurations = numSteps;
+            mStepDurations = new long[numSteps];
+            System.arraycopy(steps, 0, mStepDurations, 0, numSteps);
+        }
+
+        public long getDurationAt(int index) {
+            return mStepDurations[index] & STEP_LEVEL_TIME_MASK;
+        }
+
+        public int getLevelAt(int index) {
+            return (int)((mStepDurations[index] & STEP_LEVEL_LEVEL_MASK)
+                    >> STEP_LEVEL_LEVEL_SHIFT);
+        }
+
+        public int getInitModeAt(int index) {
+            return (int)((mStepDurations[index] & STEP_LEVEL_INITIAL_MODE_MASK)
+                    >> STEP_LEVEL_INITIAL_MODE_SHIFT);
+        }
+
+        public int getModModeAt(int index) {
+            return (int)((mStepDurations[index] & STEP_LEVEL_MODIFIED_MODE_MASK)
+                    >> STEP_LEVEL_MODIFIED_MODE_SHIFT);
+        }
+
+        private void appendHex(long val, int topOffset, StringBuilder out) {
+            boolean hasData = false;
+            while (topOffset >= 0) {
+                int digit = (int)( (val>>topOffset) & 0xf );
+                topOffset -= 4;
+                if (!hasData && digit == 0) {
+                    continue;
+                }
+                hasData = true;
+                if (digit >= 0 && digit <= 9) {
+                    out.append((char)('0' + digit));
+                } else {
+                    out.append((char)('a' + digit - 10));
+                }
+            }
+        }
+
+        public void encodeEntryAt(int index, StringBuilder out) {
+            long item = mStepDurations[index];
+            long duration = item & STEP_LEVEL_TIME_MASK;
+            int level = (int)((item & STEP_LEVEL_LEVEL_MASK)
+                    >> STEP_LEVEL_LEVEL_SHIFT);
+            int initMode = (int)((item & STEP_LEVEL_INITIAL_MODE_MASK)
+                    >> STEP_LEVEL_INITIAL_MODE_SHIFT);
+            int modMode = (int)((item & STEP_LEVEL_MODIFIED_MODE_MASK)
+                    >> STEP_LEVEL_MODIFIED_MODE_SHIFT);
+            switch ((initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                case Display.STATE_OFF: out.append('f'); break;
+                case Display.STATE_ON: out.append('o'); break;
+                case Display.STATE_DOZE: out.append('d'); break;
+                case Display.STATE_DOZE_SUSPEND: out.append('z'); break;
+            }
+            if ((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
+                out.append('p');
+            }
+            if ((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+                out.append('i');
+            }
+            switch ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                case Display.STATE_OFF: out.append('F'); break;
+                case Display.STATE_ON: out.append('O'); break;
+                case Display.STATE_DOZE: out.append('D'); break;
+                case Display.STATE_DOZE_SUSPEND: out.append('Z'); break;
+            }
+            if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
+                out.append('P');
+            }
+            if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+                out.append('I');
+            }
+            out.append('-');
+            appendHex(level, 4, out);
+            out.append('-');
+            appendHex(duration, STEP_LEVEL_LEVEL_SHIFT-4, out);
+        }
+
+        public void decodeEntryAt(int index, String value) {
+            final int N = value.length();
+            int i = 0;
+            char c;
+            long out = 0;
+            while (i < N && (c=value.charAt(i)) != '-') {
+                i++;
+                switch (c) {
+                    case 'f': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'o': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'd': out |= (((long)Display.STATE_DOZE-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'z': out |= (((long)Display.STATE_DOZE_SUSPEND-1)
+                            << STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'p': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
+                            << STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'i': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+                            << STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'F': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                    case 'O': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                    case 'D': out |= (((long)Display.STATE_DOZE-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                    case 'Z': out |= (((long)Display.STATE_DOZE_SUSPEND-1)
+                            << STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                    case 'P': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
+                            << STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                    case 'I': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+                            << STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                }
+            }
+            i++;
+            long level = 0;
+            while (i < N && (c=value.charAt(i)) != '-') {
+                i++;
+                level <<= 4;
+                if (c >= '0' && c <= '9') {
+                    level += c - '0';
+                } else if (c >= 'a' && c <= 'f') {
+                    level += c - 'a' + 10;
+                } else if (c >= 'A' && c <= 'F') {
+                    level += c - 'A' + 10;
+                }
+            }
+            i++;
+            out |= (level << STEP_LEVEL_LEVEL_SHIFT) & STEP_LEVEL_LEVEL_MASK;
+            long duration = 0;
+            while (i < N && (c=value.charAt(i)) != '-') {
+                i++;
+                duration <<= 4;
+                if (c >= '0' && c <= '9') {
+                    duration += c - '0';
+                } else if (c >= 'a' && c <= 'f') {
+                    duration += c - 'a' + 10;
+                } else if (c >= 'A' && c <= 'F') {
+                    duration += c - 'A' + 10;
+                }
+            }
+            mStepDurations[index] = out | (duration & STEP_LEVEL_TIME_MASK);
+        }
+
+        public void init() {
+            mLastStepTime = -1;
+            mNumStepDurations = 0;
+        }
+
+        public void clearTime() {
+            mLastStepTime = -1;
+        }
+
+        public long computeTimePerLevel() {
+            final long[] steps = mStepDurations;
+            final int numSteps = mNumStepDurations;
+
+            // For now we'll do a simple average across all steps.
+            if (numSteps <= 0) {
+                return -1;
+            }
+            long total = 0;
+            for (int i=0; i<numSteps; i++) {
+                total += steps[i] & STEP_LEVEL_TIME_MASK;
+            }
+            return total / numSteps;
+            /*
+            long[] buckets = new long[numSteps];
+            int numBuckets = 0;
+            int numToAverage = 4;
+            int i = 0;
+            while (i < numSteps) {
+                long totalTime = 0;
+                int num = 0;
+                for (int j=0; j<numToAverage && (i+j)<numSteps; j++) {
+                    totalTime += steps[i+j] & STEP_LEVEL_TIME_MASK;
+                    num++;
+                }
+                buckets[numBuckets] = totalTime / num;
+                numBuckets++;
+                numToAverage *= 2;
+                i += num;
+            }
+            if (numBuckets < 1) {
+                return -1;
+            }
+            long averageTime = buckets[numBuckets-1];
+            for (i=numBuckets-2; i>=0; i--) {
+                averageTime = (averageTime + buckets[i]) / 2;
+            }
+            return averageTime;
+            */
+        }
+
+        public long computeTimeEstimate(long modesOfInterest, long modeValues,
+                int[] outNumOfInterest) {
+            final long[] steps = mStepDurations;
+            final int count = mNumStepDurations;
+            if (count <= 0) {
+                return -1;
+            }
+            long total = 0;
+            int numOfInterest = 0;
+            for (int i=0; i<count; i++) {
+                long initMode = (steps[i] & STEP_LEVEL_INITIAL_MODE_MASK)
+                        >> STEP_LEVEL_INITIAL_MODE_SHIFT;
+                long modMode = (steps[i] & STEP_LEVEL_MODIFIED_MODE_MASK)
+                        >> STEP_LEVEL_MODIFIED_MODE_SHIFT;
+                // If the modes of interest didn't change during this step period...
+                if ((modMode&modesOfInterest) == 0) {
+                    // And the mode values during this period match those we are measuring...
+                    if ((initMode&modesOfInterest) == modeValues) {
+                        // Then this can be used to estimate the total time!
+                        numOfInterest++;
+                        total += steps[i] & STEP_LEVEL_TIME_MASK;
+                    }
+                }
+            }
+            if (numOfInterest <= 0) {
+                return -1;
+            }
+
+            if (outNumOfInterest != null) {
+                outNumOfInterest[0] = numOfInterest;
+            }
+
+            // The estimated time is the average time we spend in each level, multipled
+            // by 100 -- the total number of battery levels
+            return (total / numOfInterest) * 100;
+        }
+
+        public void addLevelSteps(int numStepLevels, long modeBits, long elapsedRealtime) {
+            int stepCount = mNumStepDurations;
+            final long lastStepTime = mLastStepTime;
+            if (lastStepTime >= 0 && numStepLevels > 0) {
+                final long[] steps = mStepDurations;
+                long duration = elapsedRealtime - lastStepTime;
+                for (int i=0; i<numStepLevels; i++) {
+                    System.arraycopy(steps, 0, steps, 1, steps.length-1);
+                    long thisDuration = duration / (numStepLevels-i);
+                    duration -= thisDuration;
+                    if (thisDuration > STEP_LEVEL_TIME_MASK) {
+                        thisDuration = STEP_LEVEL_TIME_MASK;
+                    }
+                    steps[0] = thisDuration | modeBits;
+                }
+                stepCount += numStepLevels;
+                if (stepCount > steps.length) {
+                    stepCount = steps.length;
+                }
+            }
+            mNumStepDurations = stepCount;
+            mLastStepTime = elapsedRealtime;
+        }
+
+        public void readFromParcel(Parcel in) {
+            final int N = in.readInt();
+            if (N > mStepDurations.length) {
+                throw new ParcelFormatException("more step durations than available: " + N);
+            }
+            mNumStepDurations = N;
+            for (int i=0; i<N; i++) {
+                mStepDurations[i] = in.readLong();
+            }
+        }
+
+        public void writeToParcel(Parcel out) {
+            final int N = mNumStepDurations;
+            out.writeInt(N);
+            for (int i=0; i<N; i++) {
+                out.writeLong(mStepDurations[i]);
+            }
+        }
+    }
+
+    public static final class PackageChange {
+        public String mPackageName;
+        public boolean mUpdate;
+        public long mVersionCode;
+    }
+
+    public static final class DailyItem {
+        public long mStartTime;
+        public long mEndTime;
+        public LevelStepTracker mDischargeSteps;
+        public LevelStepTracker mChargeSteps;
+        public ArrayList<PackageChange> mPackageChanges;
+    }
+
+    public abstract DailyItem getDailyItemLocked(int daysAgo);
+
+    public abstract long getCurrentDailyStartTime();
+
+    public abstract long getNextMinDailyDeadline();
+
+    public abstract long getNextMaxDailyDeadline();
+
+    /**
+     * Returns the total number of frequencies across all CPU clusters.
+     */
+    public abstract int getCpuFreqCount();
+
+    public abstract long[] getCpuFreqs();
+
+    public final static class HistoryTag {
+        public String string;
+        public int uid;
+
+        public int poolIdx;
+
+        public void setTo(HistoryTag o) {
+            string = o.string;
+            uid = o.uid;
+            poolIdx = o.poolIdx;
+        }
+
+        public void setTo(String _string, int _uid) {
+            string = _string;
+            uid = _uid;
+            poolIdx = -1;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(string);
+            dest.writeInt(uid);
+        }
+
+        public void readFromParcel(Parcel src) {
+            string = src.readString();
+            uid = src.readInt();
+            poolIdx = -1;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            HistoryTag that = (HistoryTag) o;
+
+            if (uid != that.uid) return false;
+            if (!string.equals(that.string)) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = string.hashCode();
+            result = 31 * result + uid;
+            return result;
+        }
+    }
+
+    /**
+     * Optional detailed information that can go into a history step.  This is typically
+     * generated each time the battery level changes.
+     */
+    public final static class HistoryStepDetails {
+        // Time (in 1/100 second) spent in user space and the kernel since the last step.
+        public int userTime;
+        public int systemTime;
+
+        // Top three apps using CPU in the last step, with times in 1/100 second.
+        public int appCpuUid1;
+        public int appCpuUTime1;
+        public int appCpuSTime1;
+        public int appCpuUid2;
+        public int appCpuUTime2;
+        public int appCpuSTime2;
+        public int appCpuUid3;
+        public int appCpuUTime3;
+        public int appCpuSTime3;
+
+        // Information from /proc/stat
+        public int statUserTime;
+        public int statSystemTime;
+        public int statIOWaitTime;
+        public int statIrqTime;
+        public int statSoftIrqTime;
+        public int statIdlTime;
+
+        // Low power state stats
+        public String statSubsystemPowerState;
+
+        public HistoryStepDetails() {
+            clear();
+        }
+
+        public void clear() {
+            userTime = systemTime = 0;
+            appCpuUid1 = appCpuUid2 = appCpuUid3 = -1;
+            appCpuUTime1 = appCpuSTime1 = appCpuUTime2 = appCpuSTime2
+                    = appCpuUTime3 = appCpuSTime3 = 0;
+        }
+
+        public void writeToParcel(Parcel out) {
+            out.writeInt(userTime);
+            out.writeInt(systemTime);
+            out.writeInt(appCpuUid1);
+            out.writeInt(appCpuUTime1);
+            out.writeInt(appCpuSTime1);
+            out.writeInt(appCpuUid2);
+            out.writeInt(appCpuUTime2);
+            out.writeInt(appCpuSTime2);
+            out.writeInt(appCpuUid3);
+            out.writeInt(appCpuUTime3);
+            out.writeInt(appCpuSTime3);
+            out.writeInt(statUserTime);
+            out.writeInt(statSystemTime);
+            out.writeInt(statIOWaitTime);
+            out.writeInt(statIrqTime);
+            out.writeInt(statSoftIrqTime);
+            out.writeInt(statIdlTime);
+            out.writeString(statSubsystemPowerState);
+        }
+
+        public void readFromParcel(Parcel in) {
+            userTime = in.readInt();
+            systemTime = in.readInt();
+            appCpuUid1 = in.readInt();
+            appCpuUTime1 = in.readInt();
+            appCpuSTime1 = in.readInt();
+            appCpuUid2 = in.readInt();
+            appCpuUTime2 = in.readInt();
+            appCpuSTime2 = in.readInt();
+            appCpuUid3 = in.readInt();
+            appCpuUTime3 = in.readInt();
+            appCpuSTime3 = in.readInt();
+            statUserTime = in.readInt();
+            statSystemTime = in.readInt();
+            statIOWaitTime = in.readInt();
+            statIrqTime = in.readInt();
+            statSoftIrqTime = in.readInt();
+            statIdlTime = in.readInt();
+            statSubsystemPowerState = in.readString();
+        }
+    }
+
+    /**
+     * Measured energy delta from the previous reading.
+     */
+    public static final class EnergyConsumerDetails {
+        /**
+         * Description of the energy consumer, such as CPU, DISPLAY etc
+         */
+        public static final class EnergyConsumer {
+            /**
+             * See android.hardware.power.stats.EnergyConsumerType
+             */
+            public int type;
+            /**
+             * Used when there are multipe energy consumers of the same type, such
+             * as CPU clusters, multiple displays on foldable devices etc.
+             */
+            public int ordinal;
+            /**
+             * Human-readable name of the energy consumer, e.g. "CPU"
+             */
+            public String name;
+        }
+        public EnergyConsumer[] consumers;
+        public long[] chargeUC;
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < consumers.length; i++) {
+                if (chargeUC[i] == POWER_DATA_UNAVAILABLE) {
+                    continue;
+                }
+                if (sb.length() != 0) {
+                    sb.append(' ');
+                }
+                sb.append(consumers[i].name);
+                sb.append('=');
+                sb.append(chargeUC[i]);
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * CPU usage for a given UID.
+     */
+    public static final class CpuUsageDetails {
+        /**
+         * Descriptions of CPU power brackets, see PowerProfile.getCpuPowerBracketDescription
+         */
+        public String[] cpuBracketDescriptions;
+        public int uid;
+        /**
+         *  The delta, in milliseconds, per CPU power bracket, from the previous record for the
+         *  same UID.
+         */
+        public long[] cpuUsageMs;
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            UserHandle.formatUid(sb, uid);
+            sb.append(": ");
+            for (int bracket = 0; bracket < cpuUsageMs.length; bracket++) {
+                if (bracket != 0) {
+                    sb.append(", ");
+                }
+                sb.append(cpuUsageMs[bracket]);
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Battery history record.
+     */
+    public static final class HistoryItem {
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+        public HistoryItem next;
+
+        // The time of this event in milliseconds, as per SystemClock.elapsedRealtime().
+        @UnsupportedAppUsage
+        public long time;
+
+        @UnsupportedAppUsage
+        public static final byte CMD_UPDATE = 0;        // These can be written as deltas
+        public static final byte CMD_NULL = -1;
+        public static final byte CMD_START = 4;
+        public static final byte CMD_CURRENT_TIME = 5;
+        public static final byte CMD_OVERFLOW = 6;
+        public static final byte CMD_RESET = 7;
+        public static final byte CMD_SHUTDOWN = 8;
+
+        @UnsupportedAppUsage
+        public byte cmd = CMD_NULL;
+
+        /**
+         * Return whether the command code is a delta data update.
+         */
+        public boolean isDeltaData() {
+            return cmd == CMD_UPDATE;
+        }
+
+        @UnsupportedAppUsage
+        public byte batteryLevel;
+        @UnsupportedAppUsage
+        public byte batteryStatus;
+        @UnsupportedAppUsage
+        public byte batteryHealth;
+        @UnsupportedAppUsage
+        public byte batteryPlugType;
+
+        public short batteryTemperature;
+        // Battery voltage in millivolts (mV).
+        @UnsupportedAppUsage
+        public char batteryVoltage;
+
+        // The charge of the battery in micro-Ampere-hours.
+        public int batteryChargeUah;
+
+        public double modemRailChargeMah;
+        public double wifiRailChargeMah;
+
+        // Constants from SCREEN_BRIGHTNESS_*
+        public static final int STATE_BRIGHTNESS_SHIFT = 0;
+        public static final int STATE_BRIGHTNESS_MASK = 0x7;
+        // Constants from SIGNAL_STRENGTH_*
+        public static final int STATE_PHONE_SIGNAL_STRENGTH_SHIFT = 3;
+        public static final int STATE_PHONE_SIGNAL_STRENGTH_MASK = 0x7 << STATE_PHONE_SIGNAL_STRENGTH_SHIFT;
+        // Constants from ServiceState.STATE_*
+        public static final int STATE_PHONE_STATE_SHIFT = 6;
+        public static final int STATE_PHONE_STATE_MASK = 0x7 << STATE_PHONE_STATE_SHIFT;
+        // Constants from DATA_CONNECTION_*
+        public static final int STATE_DATA_CONNECTION_SHIFT = 9;
+        public static final int STATE_DATA_CONNECTION_MASK = 0x1f << STATE_DATA_CONNECTION_SHIFT;
+
+        // These states always appear directly in the first int token
+        // of a delta change; they should be ones that change relatively
+        // frequently.
+        public static final int STATE_CPU_RUNNING_FLAG = 1<<31;
+        public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
+        public static final int STATE_GPS_ON_FLAG = 1<<29;
+        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28;
+        public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
+        public static final int STATE_WIFI_RADIO_ACTIVE_FLAG = 1<<26;
+        public static final int STATE_MOBILE_RADIO_ACTIVE_FLAG = 1<<25;
+        // Do not use, this is used for coulomb delta count.
+        private static final int STATE_RESERVED_0 = 1<<24;
+        // These are on the lower bits used for the command; if they change
+        // we need to write another int of data.
+        public static final int STATE_SENSOR_ON_FLAG = 1<<23;
+        public static final int STATE_AUDIO_ON_FLAG = 1<<22;
+        public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
+        public static final int STATE_SCREEN_ON_FLAG = 1<<20;       // consider moving to states2
+        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; // consider moving to states2
+        public static final int STATE_SCREEN_DOZE_FLAG = 1 << 18;
+        // empty slot
+        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<16;
+
+        public static final int MOST_INTERESTING_STATES =
+                STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG | STATE_SCREEN_DOZE_FLAG;
+
+        public static final int SETTLE_TO_ZERO_STATES = 0xffff0000 & ~MOST_INTERESTING_STATES;
+
+        @UnsupportedAppUsage
+        public int states;
+
+        // Constants from WIFI_SUPPL_STATE_*
+        public static final int STATE2_WIFI_SUPPL_STATE_SHIFT = 0;
+        public static final int STATE2_WIFI_SUPPL_STATE_MASK = 0xf;
+        // Values for NUM_WIFI_SIGNAL_STRENGTH_BINS
+        public static final int STATE2_WIFI_SIGNAL_STRENGTH_SHIFT = 4;
+        public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK =
+                0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT;
+        // Values for NUM_GPS_SIGNAL_QUALITY_LEVELS
+        public static final int STATE2_GPS_SIGNAL_QUALITY_SHIFT = 7;
+        public static final int STATE2_GPS_SIGNAL_QUALITY_MASK =
+            0x1 << STATE2_GPS_SIGNAL_QUALITY_SHIFT;
+
+        public static final int STATE2_POWER_SAVE_FLAG = 1<<31;
+        public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
+        public static final int STATE2_WIFI_RUNNING_FLAG = 1<<29;
+        public static final int STATE2_WIFI_ON_FLAG = 1<<28;
+        public static final int STATE2_FLASHLIGHT_FLAG = 1<<27;
+        public static final int STATE2_DEVICE_IDLE_SHIFT = 25;
+        public static final int STATE2_DEVICE_IDLE_MASK = 0x3 << STATE2_DEVICE_IDLE_SHIFT;
+        public static final int STATE2_CHARGING_FLAG = 1<<24;
+        public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<23;
+        public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22;
+        public static final int STATE2_CAMERA_FLAG = 1<<21;
+        public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
+        public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
+        public static final int STATE2_USB_DATA_LINK_FLAG = 1 << 18;
+        public static final int STATE2_EXTENSIONS_FLAG = 1 << 17;
+
+        public static final int MOST_INTERESTING_STATES2 =
+                STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
+                | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
+
+        public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2;
+
+        @UnsupportedAppUsage
+        public int states2;
+
+        // The wake lock that was acquired at this point.
+        public HistoryTag wakelockTag;
+
+        // Kernel wakeup reason at this point.
+        public HistoryTag wakeReasonTag;
+
+        // Non-null when there is more detailed information at this step.
+        public HistoryStepDetails stepDetails;
+
+        // Non-null when there is energy consumer information
+        public EnergyConsumerDetails energyConsumerDetails;
+
+        // Non-null when there is CPU usage information
+        public CpuUsageDetails cpuUsageDetails;
+
+        public static final int EVENT_FLAG_START = 0x8000;
+        public static final int EVENT_FLAG_FINISH = 0x4000;
+
+        // No event in this item.
+        public static final int EVENT_NONE = 0x0000;
+        // Event is about a process that is running.
+        public static final int EVENT_PROC = 0x0001;
+        // Event is about an application package that is in the foreground.
+        public static final int EVENT_FOREGROUND = 0x0002;
+        // Event is about an application package that is at the top of the screen.
+        public static final int EVENT_TOP = 0x0003;
+        // Event is about active sync operations.
+        public static final int EVENT_SYNC = 0x0004;
+        // Events for all additional wake locks aquired/release within a wake block.
+        // These are not generated by default.
+        public static final int EVENT_WAKE_LOCK = 0x0005;
+        // Event is about an application executing a scheduled job.
+        public static final int EVENT_JOB = 0x0006;
+        // Events for users running.
+        public static final int EVENT_USER_RUNNING = 0x0007;
+        // Events for foreground user.
+        public static final int EVENT_USER_FOREGROUND = 0x0008;
+        // Event for connectivity changed.
+        public static final int EVENT_CONNECTIVITY_CHANGED = 0x0009;
+        // Event for becoming active taking us out of idle mode.
+        public static final int EVENT_ACTIVE = 0x000a;
+        // Event for a package being installed.
+        public static final int EVENT_PACKAGE_INSTALLED = 0x000b;
+        // Event for a package being uninstalled.
+        public static final int EVENT_PACKAGE_UNINSTALLED = 0x000c;
+        // Event for an alarm being sent out to an app.
+        public static final int EVENT_ALARM = 0x000d;
+        // Record that we have decided we need to collect new stats data.
+        public static final int EVENT_COLLECT_EXTERNAL_STATS = 0x000e;
+        // Event for a package becoming inactive due to being unused for a period of time.
+        public static final int EVENT_PACKAGE_INACTIVE = 0x000f;
+        // Event for a package becoming active due to an interaction.
+        public static final int EVENT_PACKAGE_ACTIVE = 0x0010;
+        // Event for a package being on the temporary allowlist.
+        public static final int EVENT_TEMP_WHITELIST = 0x0011;
+        // Event for the screen waking up.
+        public static final int EVENT_SCREEN_WAKE_UP = 0x0012;
+        // Event for the UID that woke up the application processor.
+        // Used for wakeups coming from WiFi, modem, etc.
+        public static final int EVENT_WAKEUP_AP = 0x0013;
+        // Event for reporting that a specific partial wake lock has been held for a long duration.
+        public static final int EVENT_LONG_WAKE_LOCK = 0x0014;
+
+        // Number of event types.
+        public static final int EVENT_COUNT = 0x0016;
+        // Mask to extract out only the type part of the event.
+        public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
+
+        public static final int EVENT_PROC_START = EVENT_PROC | EVENT_FLAG_START;
+        public static final int EVENT_PROC_FINISH = EVENT_PROC | EVENT_FLAG_FINISH;
+        public static final int EVENT_FOREGROUND_START = EVENT_FOREGROUND | EVENT_FLAG_START;
+        public static final int EVENT_FOREGROUND_FINISH = EVENT_FOREGROUND | EVENT_FLAG_FINISH;
+        public static final int EVENT_TOP_START = EVENT_TOP | EVENT_FLAG_START;
+        public static final int EVENT_TOP_FINISH = EVENT_TOP | EVENT_FLAG_FINISH;
+        public static final int EVENT_SYNC_START = EVENT_SYNC | EVENT_FLAG_START;
+        public static final int EVENT_SYNC_FINISH = EVENT_SYNC | EVENT_FLAG_FINISH;
+        public static final int EVENT_WAKE_LOCK_START = EVENT_WAKE_LOCK | EVENT_FLAG_START;
+        public static final int EVENT_WAKE_LOCK_FINISH = EVENT_WAKE_LOCK | EVENT_FLAG_FINISH;
+        public static final int EVENT_JOB_START = EVENT_JOB | EVENT_FLAG_START;
+        public static final int EVENT_JOB_FINISH = EVENT_JOB | EVENT_FLAG_FINISH;
+        public static final int EVENT_USER_RUNNING_START = EVENT_USER_RUNNING | EVENT_FLAG_START;
+        public static final int EVENT_USER_RUNNING_FINISH = EVENT_USER_RUNNING | EVENT_FLAG_FINISH;
+        public static final int EVENT_USER_FOREGROUND_START =
+                EVENT_USER_FOREGROUND | EVENT_FLAG_START;
+        public static final int EVENT_USER_FOREGROUND_FINISH =
+                EVENT_USER_FOREGROUND | EVENT_FLAG_FINISH;
+        public static final int EVENT_ALARM_START = EVENT_ALARM | EVENT_FLAG_START;
+        public static final int EVENT_ALARM_FINISH = EVENT_ALARM | EVENT_FLAG_FINISH;
+        public static final int EVENT_TEMP_WHITELIST_START =
+                EVENT_TEMP_WHITELIST | EVENT_FLAG_START;
+        public static final int EVENT_TEMP_WHITELIST_FINISH =
+                EVENT_TEMP_WHITELIST | EVENT_FLAG_FINISH;
+        public static final int EVENT_LONG_WAKE_LOCK_START =
+                EVENT_LONG_WAKE_LOCK | EVENT_FLAG_START;
+        public static final int EVENT_LONG_WAKE_LOCK_FINISH =
+                EVENT_LONG_WAKE_LOCK | EVENT_FLAG_FINISH;
+
+        // For CMD_EVENT.
+        public int eventCode;
+        public HistoryTag eventTag;
+
+        // Only set for CMD_CURRENT_TIME or CMD_RESET, as per System.currentTimeMillis().
+        public long currentTime;
+
+        // Meta-data when reading.
+        public int numReadInts;
+
+        // Pre-allocated objects.
+        public final HistoryTag localWakelockTag = new HistoryTag();
+        public final HistoryTag localWakeReasonTag = new HistoryTag();
+        public final HistoryTag localEventTag = new HistoryTag();
+
+        // Includes a tag's first occurrence in the parcel, so the value of the tag is written
+        // rather than just its index in the history tag pool.
+        public boolean tagsFirstOccurrence;
+
+        @UnsupportedAppUsage
+        public HistoryItem() {
+        }
+
+        public HistoryItem(Parcel src) {
+            readFromParcel(src);
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeLong(time);
+            int bat = (((int)cmd)&0xff)
+                    | ((((int)batteryLevel)<<8)&0xff00)
+                    | ((((int)batteryStatus)<<16)&0xf0000)
+                    | ((((int)batteryHealth)<<20)&0xf00000)
+                    | ((((int)batteryPlugType)<<24)&0xf000000)
+                    | (wakelockTag != null ? 0x10000000 : 0)
+                    | (wakeReasonTag != null ? 0x20000000 : 0)
+                    | (eventCode != EVENT_NONE ? 0x40000000 : 0);
+            dest.writeInt(bat);
+            bat = (((int)batteryTemperature)&0xffff)
+                    | ((((int)batteryVoltage)<<16)&0xffff0000);
+            dest.writeInt(bat);
+            dest.writeInt(batteryChargeUah);
+            dest.writeDouble(modemRailChargeMah);
+            dest.writeDouble(wifiRailChargeMah);
+            dest.writeInt(states);
+            dest.writeInt(states2);
+            if (wakelockTag != null) {
+                wakelockTag.writeToParcel(dest, flags);
+            }
+            if (wakeReasonTag != null) {
+                wakeReasonTag.writeToParcel(dest, flags);
+            }
+            if (eventCode != EVENT_NONE) {
+                dest.writeInt(eventCode);
+                eventTag.writeToParcel(dest, flags);
+            }
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
+                dest.writeLong(currentTime);
+            }
+        }
+
+        public void readFromParcel(Parcel src) {
+            int start = src.dataPosition();
+            time = src.readLong();
+            int bat = src.readInt();
+            cmd = (byte)(bat&0xff);
+            batteryLevel = (byte)((bat>>8)&0xff);
+            batteryStatus = (byte)((bat>>16)&0xf);
+            batteryHealth = (byte)((bat>>20)&0xf);
+            batteryPlugType = (byte)((bat>>24)&0xf);
+            int bat2 = src.readInt();
+            batteryTemperature = (short)(bat2&0xffff);
+            batteryVoltage = (char)((bat2>>16)&0xffff);
+            batteryChargeUah = src.readInt();
+            modemRailChargeMah = src.readDouble();
+            wifiRailChargeMah = src.readDouble();
+            states = src.readInt();
+            states2 = src.readInt();
+            if ((bat&0x10000000) != 0) {
+                wakelockTag = localWakelockTag;
+                wakelockTag.readFromParcel(src);
+            } else {
+                wakelockTag = null;
+            }
+            if ((bat&0x20000000) != 0) {
+                wakeReasonTag = localWakeReasonTag;
+                wakeReasonTag.readFromParcel(src);
+            } else {
+                wakeReasonTag = null;
+            }
+            if ((bat&0x40000000) != 0) {
+                eventCode = src.readInt();
+                eventTag = localEventTag;
+                eventTag.readFromParcel(src);
+            } else {
+                eventCode = EVENT_NONE;
+                eventTag = null;
+            }
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
+                currentTime = src.readLong();
+            } else {
+                currentTime = 0;
+            }
+            numReadInts += (src.dataPosition()-start)/4;
+        }
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+        public void clear() {
+            time = 0;
+            cmd = CMD_NULL;
+            batteryLevel = 0;
+            batteryStatus = 0;
+            batteryHealth = 0;
+            batteryPlugType = 0;
+            batteryTemperature = 0;
+            batteryVoltage = 0;
+            batteryChargeUah = 0;
+            modemRailChargeMah = 0;
+            wifiRailChargeMah = 0;
+            states = 0;
+            states2 = 0;
+            wakelockTag = null;
+            wakeReasonTag = null;
+            eventCode = EVENT_NONE;
+            eventTag = null;
+            tagsFirstOccurrence = false;
+            energyConsumerDetails = null;
+            cpuUsageDetails = null;
+        }
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+        public void setTo(HistoryItem o) {
+            time = o.time;
+            cmd = o.cmd;
+            setToCommon(o);
+        }
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+        public void setTo(long time, byte cmd, HistoryItem o) {
+            this.time = time;
+            this.cmd = cmd;
+            setToCommon(o);
+        }
+
+        private void setToCommon(HistoryItem o) {
+            batteryLevel = o.batteryLevel;
+            batteryStatus = o.batteryStatus;
+            batteryHealth = o.batteryHealth;
+            batteryPlugType = o.batteryPlugType;
+            batteryTemperature = o.batteryTemperature;
+            batteryVoltage = o.batteryVoltage;
+            batteryChargeUah = o.batteryChargeUah;
+            modemRailChargeMah = o.modemRailChargeMah;
+            wifiRailChargeMah = o.wifiRailChargeMah;
+            states = o.states;
+            states2 = o.states2;
+            if (o.wakelockTag != null) {
+                wakelockTag = localWakelockTag;
+                wakelockTag.setTo(o.wakelockTag);
+            } else {
+                wakelockTag = null;
+            }
+            if (o.wakeReasonTag != null) {
+                wakeReasonTag = localWakeReasonTag;
+                wakeReasonTag.setTo(o.wakeReasonTag);
+            } else {
+                wakeReasonTag = null;
+            }
+            eventCode = o.eventCode;
+            if (o.eventTag != null) {
+                eventTag = localEventTag;
+                eventTag.setTo(o.eventTag);
+            } else {
+                eventTag = null;
+            }
+            tagsFirstOccurrence = o.tagsFirstOccurrence;
+            currentTime = o.currentTime;
+            energyConsumerDetails = o.energyConsumerDetails;
+            cpuUsageDetails = o.cpuUsageDetails;
+        }
+
+        public boolean sameNonEvent(HistoryItem o) {
+            return batteryLevel == o.batteryLevel
+                    && batteryStatus == o.batteryStatus
+                    && batteryHealth == o.batteryHealth
+                    && batteryPlugType == o.batteryPlugType
+                    && batteryTemperature == o.batteryTemperature
+                    && batteryVoltage == o.batteryVoltage
+                    && batteryChargeUah == o.batteryChargeUah
+                    && modemRailChargeMah == o.modemRailChargeMah
+                    && wifiRailChargeMah == o.wifiRailChargeMah
+                    && states == o.states
+                    && states2 == o.states2
+                    && currentTime == o.currentTime;
+        }
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+        public boolean same(HistoryItem o) {
+            if (!sameNonEvent(o) || eventCode != o.eventCode) {
+                return false;
+            }
+            if (wakelockTag != o.wakelockTag) {
+                if (wakelockTag == null || o.wakelockTag == null) {
+                    return false;
+                }
+                if (!wakelockTag.equals(o.wakelockTag)) {
+                    return false;
+                }
+            }
+            if (wakeReasonTag != o.wakeReasonTag) {
+                if (wakeReasonTag == null || o.wakeReasonTag == null) {
+                    return false;
+                }
+                if (!wakeReasonTag.equals(o.wakeReasonTag)) {
+                    return false;
+                }
+            }
+            if (eventTag != o.eventTag) {
+                if (eventTag == null || o.eventTag == null) {
+                    return false;
+                }
+                if (!eventTag.equals(o.eventTag)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    public final static class HistoryEventTracker {
+        private final HashMap<String, SparseIntArray>[] mActiveEvents
+                = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+
+        public boolean updateState(int code, String name, int uid, int poolIdx) {
+            if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    active = new HashMap<>();
+                    mActiveEvents[idx] = active;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    uids = new SparseIntArray();
+                    active.put(name, uids);
+                }
+                if (uids.indexOfKey(uid) >= 0) {
+                    // Already set, nothing to do!
+                    return false;
+                }
+                uids.put(uid, poolIdx);
+            } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                idx = uids.indexOfKey(uid);
+                if (idx < 0) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                uids.removeAt(idx);
+                if (uids.size() <= 0) {
+                    active.remove(name);
+                }
+            }
+            return true;
+        }
+
+        public void removeEvents(int code) {
+            int idx = code&HistoryItem.EVENT_TYPE_MASK;
+            mActiveEvents[idx] = null;
+        }
+
+        public HashMap<String, SparseIntArray> getStateForEvent(int code) {
+            return mActiveEvents[code];
+        }
+    }
+
+    public static final class BitDescription {
+        public final int mask;
+        public final int shift;
+        public final String name;
+        public final String shortName;
+        public final String[] values;
+        public final String[] shortValues;
+
+        public BitDescription(int mask, String name, String shortName) {
+            this.mask = mask;
+            this.shift = -1;
+            this.name = name;
+            this.shortName = shortName;
+            this.values = null;
+            this.shortValues = null;
+        }
+
+        public BitDescription(int mask, int shift, String name, String shortName,
+                String[] values, String[] shortValues) {
+            this.mask = mask;
+            this.shift = shift;
+            this.name = name;
+            this.shortName = shortName;
+            this.values = values;
+            this.shortValues = shortValues;
+        }
+    }
+
+    /**
+     * Don't allow any more batching in to the current history event.  This
+     * is called when printing partial histories, so to ensure that the next
+     * history event will go in to a new batch after what was printed in the
+     * last partial history.
+     */
+    public abstract void commitCurrentHistoryBatchLocked();
+
+    public abstract int getHistoryTotalSize();
+
+    public abstract int getHistoryUsedSize();
+
+    public abstract int getHistoryStringPoolSize();
+
+    public abstract int getHistoryStringPoolBytes();
+
+    public abstract String getHistoryTagPoolString(int index);
+
+    public abstract int getHistoryTagPoolUid(int index);
+
+    /**
+     * Returns a BatteryStatsHistoryIterator. Battery history will continue being writable,
+     * but the iterator will continue iterating over the snapshot taken at the time this method
+     * is called.
+     */
+    public abstract BatteryStatsHistoryIterator iterateBatteryStatsHistory();
+
+    /**
+     * Returns the number of times the device has been started.
+     */
+    public abstract int getStartCount();
+
+    /**
+     * Returns the time in microseconds that the screen has been on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public abstract long getScreenOnTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times the screen was turned on.
+     *
+     * {@hide}
+     */
+    public abstract int getScreenOnCount(int which);
+
+    /**
+     * Returns the time in microseconds that the screen has been dozing while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getScreenDozeTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times the screen was turned dozing.
+     *
+     * {@hide}
+     */
+    public abstract int getScreenDozeCount(int which);
+
+    public abstract long getInteractiveTime(long elapsedRealtimeUs, int which);
+
+    public static final int SCREEN_BRIGHTNESS_DARK = 0;
+    public static final int SCREEN_BRIGHTNESS_DIM = 1;
+    public static final int SCREEN_BRIGHTNESS_MEDIUM = 2;
+    public static final int SCREEN_BRIGHTNESS_LIGHT = 3;
+    public static final int SCREEN_BRIGHTNESS_BRIGHT = 4;
+
+    static final String[] SCREEN_BRIGHTNESS_NAMES = {
+        "dark", "dim", "medium", "light", "bright"
+    };
+
+    static final String[] SCREEN_BRIGHTNESS_SHORT_NAMES = {
+        "0", "1", "2", "3", "4"
+    };
+
+    @UnsupportedAppUsage
+    public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5;
+
+    /**
+     * Returns the time in microseconds that the screen has been on with
+     * the given brightness
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public abstract long getScreenBrightnessTime(int brightnessBin,
+            long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the {@link Timer} object that tracks the given screen brightness.
+     *
+     * {@hide}
+     */
+    public abstract Timer getScreenBrightnessTimer(int brightnessBin);
+
+    /**
+     * Returns the number of physical displays on the device.
+     *
+     * {@hide}
+     */
+    public abstract int getDisplayCount();
+
+    /**
+     * Returns the time in microseconds that the screen has been on for a display while the
+     * device was running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getDisplayScreenOnTime(int display, long elapsedRealtimeUs);
+
+    /**
+     * Returns the time in microseconds that a display has been dozing while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getDisplayScreenDozeTime(int display, long elapsedRealtimeUs);
+
+    /**
+     * Returns the time in microseconds that a display has been on with the given brightness
+     * level while the device was running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getDisplayScreenBrightnessTime(int display, int brightnessBin,
+            long elapsedRealtimeUs);
+
+    /**
+     * Returns the time in microseconds that power save mode has been enabled while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getPowerSaveModeEnabledTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that power save mode was enabled.
+     *
+     * {@hide}
+     */
+    public abstract int getPowerSaveModeEnabledCount(int which);
+
+    /**
+     * Constant for device idle mode: not active.
+     */
+    public static final int DEVICE_IDLE_MODE_OFF = ServerProtoEnums.DEVICE_IDLE_MODE_OFF; // 0
+
+    /**
+     * Constant for device idle mode: active in lightweight mode.
+     */
+    public static final int DEVICE_IDLE_MODE_LIGHT = ServerProtoEnums.DEVICE_IDLE_MODE_LIGHT; // 1
+
+    /**
+     * Constant for device idle mode: active in full mode.
+     */
+    public static final int DEVICE_IDLE_MODE_DEEP = ServerProtoEnums.DEVICE_IDLE_MODE_DEEP; // 2
+
+    /**
+     * Returns the time in microseconds that device has been in idle mode while
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getDeviceIdleModeTime(int mode, long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the devie has gone in to idle mode.
+     *
+     * {@hide}
+     */
+    public abstract int getDeviceIdleModeCount(int mode, int which);
+
+    /**
+     * Return the longest duration we spent in a particular device idle mode (fully in the
+     * mode, not in idle maintenance etc).
+     */
+    public abstract long getLongestDeviceIdleModeTime(int mode);
+
+    /**
+     * Returns the time in microseconds that device has been in idling while on
+     * battery.  This is broader than {@link #getDeviceIdleModeTime} -- it
+     * counts all of the time that we consider the device to be idle, whether or not
+     * it is currently in the actual device idle mode.
+     *
+     * {@hide}
+     */
+    public abstract long getDeviceIdlingTime(int mode, long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the device has started idling.
+     *
+     * {@hide}
+     */
+    public abstract int getDeviceIdlingCount(int mode, int which);
+
+    /**
+     * Returns the number of times that connectivity state changed.
+     *
+     * {@hide}
+     */
+    public abstract int getNumConnectivityChange(int which);
+
+
+    /**
+     * Returns the time in microseconds that the phone has been running with
+     * the given GPS signal quality level
+     *
+     * {@hide}
+     */
+    public abstract long getGpsSignalQualityTime(int strengthBin,
+        long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the GPS battery drain in mA-ms
+     *
+     * {@hide}
+     */
+    public abstract long getGpsBatteryDrainMaMs();
+
+    /**
+     * Returns the time in microseconds that the phone has been on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public abstract long getPhoneOnTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times a phone call was activated.
+     *
+     * {@hide}
+     */
+    public abstract int getPhoneOnCount(int which);
+
+    /**
+     * Returns the time in microseconds that the phone has been running with
+     * the given signal strength.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public abstract long getPhoneSignalStrengthTime(int strengthBin,
+            long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the time in microseconds that the phone has been trying to
+     * acquire a signal.
+     *
+     * {@hide}
+     */
+    public abstract long getPhoneSignalScanningTime(
+            long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the {@link Timer} object that tracks how much the phone has been trying to
+     * acquire a signal.
+     *
+     * {@hide}
+     */
+    public abstract Timer getPhoneSignalScanningTimer();
+
+    /**
+     * Returns the number of times the phone has entered the given signal strength.
+     *
+     * {@hide}
+     */
+    public abstract int getPhoneSignalStrengthCount(int strengthBin, int which);
+
+    /**
+     * Return the {@link Timer} object used to track the given signal strength's duration and
+     * counts.
+     */
+    protected abstract Timer getPhoneSignalStrengthTimer(int strengthBin);
+
+    /**
+     * Returns the time in microseconds that the mobile network has been active
+     * (in a high power state).
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public abstract long getMobileRadioActiveTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the mobile network has transitioned to the
+     * active state.
+     *
+     * {@hide}
+     */
+    public abstract int getMobileRadioActiveCount(int which);
+
+    /**
+     * Returns the time in microseconds that is the difference between the mobile radio
+     * time we saw based on the elapsed timestamp when going down vs. the given time stamp
+     * from the radio.
+     *
+     * {@hide}
+     */
+    public abstract long getMobileRadioActiveAdjustedTime(int which);
+
+    /**
+     * Returns the time in microseconds that the mobile network has been active
+     * (in a high power state) but not being able to blame on an app.
+     *
+     * {@hide}
+     */
+    public abstract long getMobileRadioActiveUnknownTime(int which);
+
+    /**
+     * Return count of number of times radio was up that could not be blamed on apps.
+     *
+     * {@hide}
+     */
+    public abstract int getMobileRadioActiveUnknownCount(int which);
+
+    public static final int DATA_CONNECTION_OUT_OF_SERVICE = 0;
+    public static final int DATA_CONNECTION_EMERGENCY_SERVICE =
+            TelephonyManager.getAllNetworkTypes().length + 1;
+    public static final int DATA_CONNECTION_OTHER = DATA_CONNECTION_EMERGENCY_SERVICE + 1;
+
+
+    static final String[] DATA_CONNECTION_NAMES = {
+        "oos", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
+        "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
+        "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "nr",
+        "emngcy", "other"
+    };
+
+    @UnsupportedAppUsage
+    public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER + 1;
+
+    /**
+     * Returns the time in microseconds that the phone has been running with
+     * the given data connection.
+     *
+     * {@hide}
+     */
+    public abstract long getPhoneDataConnectionTime(int dataType,
+            long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times the phone has entered the given data
+     * connection type.
+     *
+     * {@hide}
+     */
+    public abstract int getPhoneDataConnectionCount(int dataType, int which);
+
+    /**
+     * Returns the {@link Timer} object that tracks the phone's data connection type stats.
+     */
+    public abstract Timer getPhoneDataConnectionTimer(int dataType);
+
+    /** @hide */
+    public static final int RADIO_ACCESS_TECHNOLOGY_OTHER = 0;
+    /** @hide */
+    public static final int RADIO_ACCESS_TECHNOLOGY_LTE = 1;
+    /** @hide */
+    public static final int RADIO_ACCESS_TECHNOLOGY_NR = 2;
+    /** @hide */
+    public static final int RADIO_ACCESS_TECHNOLOGY_COUNT = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "RADIO_ACCESS_TECHNOLOGY_",
+            value = {RADIO_ACCESS_TECHNOLOGY_OTHER, RADIO_ACCESS_TECHNOLOGY_LTE,
+                    RADIO_ACCESS_TECHNOLOGY_NR})
+    public @interface RadioAccessTechnology {
+    }
+
+    /** @hide */
+    public static final String[] RADIO_ACCESS_TECHNOLOGY_NAMES = {"Other", "LTE", "NR"};
+
+    /**
+     * Returns the time in milliseconds that the mobile radio has been active on a
+     * given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given
+     * transmission power level.
+     *
+     * @param rat            Radio Access Technology {@see RadioAccessTechnology}
+     * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for
+     *                       RADIO_ACCESS_TECHNOLOGY_NR. Use
+     *                       {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access
+     *                       Technologies.
+     * @param signalStrength the cellular signal strength. {@see CellSignalStrength#getLevel()}
+     * @param elapsedRealtimeMs current elapsed realtime
+     * @return time (in milliseconds) the mobile radio spent active in the specified state,
+     *         while on battery.
+     * @hide
+     */
+    public abstract long getActiveRadioDurationMs(@RadioAccessTechnology int rat,
+            @ServiceState.FrequencyRange int frequencyRange, int signalStrength,
+            long elapsedRealtimeMs);
+
+    /**
+     * Returns the time in milliseconds that the mobile radio has been actively transmitting data on
+     * a given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given
+     * transmission power level.
+     *
+     * @param rat            Radio Access Technology {@see RadioAccessTechnology}
+     * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for
+     *                       RADIO_ACCESS_TECHNOLOGY_NR. Use
+     *                       {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access
+     *                       Technologies.
+     * @param signalStrength the cellular signal strength. {@see CellSignalStrength#getLevel()}
+     * @param elapsedRealtimeMs current elapsed realtime
+     * @return time (in milliseconds) the mobile radio spent actively transmitting data in the
+     *         specified state, while on battery. Returns {@link DURATION_UNAVAILABLE} if
+     *         data unavailable.
+     * @hide
+     */
+    public abstract long getActiveTxRadioDurationMs(@RadioAccessTechnology int rat,
+            @ServiceState.FrequencyRange int frequencyRange, int signalStrength,
+            long elapsedRealtimeMs);
+
+    /**
+     * Returns the time in milliseconds that the mobile radio has been actively receiving data on a
+     * given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given
+     * transmission power level.
+     *
+     * @param rat            Radio Access Technology {@see RadioAccessTechnology}
+     * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for
+     *                       RADIO_ACCESS_TECHNOLOGY_NR. Use
+     *                       {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access
+     *                       Technologies.
+     * @param elapsedRealtimeMs current elapsed realtime
+     * @return time (in milliseconds) the mobile radio spent actively receiving data in the
+     *         specified state, while on battery. Returns {@link DURATION_UNAVAILABLE} if
+     *         data unavailable.
+     * @hide
+     */
+    public abstract long getActiveRxRadioDurationMs(@RadioAccessTechnology int rat,
+            @ServiceState.FrequencyRange int frequencyRange, long elapsedRealtimeMs);
+
+    static final String[] WIFI_SUPPL_STATE_NAMES = {
+        "invalid", "disconn", "disabled", "inactive", "scanning",
+        "authenticating", "associating", "associated", "4-way-handshake",
+        "group-handshake", "completed", "dormant", "uninit"
+    };
+
+    static final String[] WIFI_SUPPL_STATE_SHORT_NAMES = {
+        "inv", "dsc", "dis", "inact", "scan",
+        "auth", "ascing", "asced", "4-way",
+        "group", "compl", "dorm", "uninit"
+    };
+
+    /**
+     * Returned value if power data is unavailable.
+     *
+     * {@hide}
+     */
+    public static final long POWER_DATA_UNAVAILABLE = -1L;
+
+    /**
+     * Returned value if duration data is unavailable.
+     *
+     * {@hide}
+     */
+    public static final long DURATION_UNAVAILABLE = -1L;
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of bluetooth, derived from on
+     * device power measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getBluetoothEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getCpuEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the GNSS, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getGnssEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the radio, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getMobileRadioEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the phone calls, derived from on device
+     * power measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getPhoneEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on
+     * device power measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getScreenOnEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the screen in doze, derived from on
+     * device power measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getScreenDozeEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of wifi, derived from on
+     * device power measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of camera, derived from on
+     * device power measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getCameraEnergyConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) that each
+     * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
+     * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}) consumed.
+     *
+     * @return charge (in microcoulombs) used by each (custom) energy consumer of type OTHER,
+     * indexed by their ordinal. Returns null if no energy reporting is supported.
+     *
+     * {@hide}
+     */
+    public abstract @Nullable long[] getCustomEnergyConsumerBatteryConsumptionUC();
+
+    /**
+     * Returns the names of all {@link android.hardware.power.stats.EnergyConsumer}'s
+     * of (custom) energy consumer type
+     * {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+     *
+     * {@hide}
+     */
+    public abstract @NonNull String[] getCustomEnergyConsumerNames();
+
+    public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
+        new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
+        new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
+        new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps", "g"),
+        new BitDescription(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG, "wifi_full_lock", "Wl"),
+        new BitDescription(HistoryItem.STATE_WIFI_SCAN_FLAG, "wifi_scan", "Ws"),
+        new BitDescription(HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG, "wifi_multicast", "Wm"),
+        new BitDescription(HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG, "wifi_radio", "Wr"),
+        new BitDescription(HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG, "mobile_radio", "Pr"),
+        new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"),
+        new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
+        new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
+        new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
+        new BitDescription(HistoryItem.STATE_SCREEN_DOZE_FLAG, "screen_doze", "Sd"),
+        new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
+                HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn",
+                DATA_CONNECTION_NAMES, DATA_CONNECTION_NAMES),
+        new BitDescription(HistoryItem.STATE_PHONE_STATE_MASK,
+                HistoryItem.STATE_PHONE_STATE_SHIFT, "phone_state", "Pst",
+                new String[] {"in", "out", "emergency", "off"},
+                new String[] {"in", "out", "em", "off"}),
+        new BitDescription(HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK,
+                HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, "phone_signal_strength", "Pss",
+                new String[] { "none", "poor", "moderate", "good", "great" },
+                new String[] { "0", "1", "2", "3", "4" }),
+        new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
+                HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness", "Sb",
+                SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
+    };
+
+    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE2_POWER_SAVE_FLAG, "power_save", "ps"),
+        new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
+        new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"),
+        new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"),
+        new BitDescription(HistoryItem.STATE2_FLASHLIGHT_FLAG, "flashlight", "fl"),
+        new BitDescription(HistoryItem.STATE2_DEVICE_IDLE_MASK,
+                HistoryItem.STATE2_DEVICE_IDLE_SHIFT, "device_idle", "di",
+                new String[] { "off", "light", "full", "???" },
+                new String[] { "off", "light", "full", "???" }),
+        new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"),
+        new BitDescription(HistoryItem.STATE2_USB_DATA_LINK_FLAG, "usb_data", "Ud"),
+        new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
+        new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
+        new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
+                HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, "wifi_signal_strength", "Wss",
+                new String[] { "0", "1", "2", "3", "4" },
+                new String[] { "0", "1", "2", "3", "4" }),
+        new BitDescription(HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK,
+                HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, "wifi_suppl", "Wsp",
+                WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
+        new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
+        new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+        new BitDescription(HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG,
+                "cellular_high_tx_power", "Chtp"),
+        new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
+            HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
+            new String[] { "poor", "good"}, new String[] { "poor", "good"})
+    };
+
+    public static final String[] HISTORY_EVENT_NAMES = new String[] {
+            "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
+            "active", "pkginst", "pkgunin", "alarm", "stats", "pkginactive", "pkgactive",
+            "tmpwhitelist", "screenwake", "wakeupap", "longwake", "est_capacity"
+    };
+
+    public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
+            "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
+            "Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa", "Etw",
+            "Esw", "Ewa", "Elw", "Eec"
+    };
+
+    @FunctionalInterface
+    public interface IntToString {
+        String applyAsString(int val);
+    }
+
+    private static final IntToString sUidToString = UserHandle::formatUid;
+    private static final IntToString sIntToString = Integer::toString;
+
+    public static final IntToString[] HISTORY_EVENT_INT_FORMATTERS = new IntToString[] {
+            sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
+            sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sIntToString,
+            sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
+            sUidToString, sUidToString, sUidToString, sIntToString
+    };
+
+    /**
+     * Returns total time for WiFi Multicast Wakelock timer.
+     * Note that this may be different from the sum of per uid timer values.
+     *
+     *  {@hide}
+     */
+    public abstract long getWifiMulticastWakelockTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns total time for WiFi Multicast Wakelock timer
+     * Note that this may be different from the sum of per uid timer values.
+     *
+     * {@hide}
+     */
+    public abstract int getWifiMulticastWakelockCount(int which);
+
+    /**
+     * Returns the time in microseconds that wifi has been on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public abstract long getWifiOnTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the time in microseconds that wifi has been active while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiActiveTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the time in microseconds that wifi has been on and the driver has
+     * been in the running state while the device was running on battery.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public abstract long getGlobalWifiRunningTime(long elapsedRealtimeUs, int which);
+
+    static final String[] WIFI_STATE_NAMES = {
+        "off", "scanning", "no_net", "disconn",
+        "sta", "p2p", "sta_p2p", "soft_ap"
+    };
+
+    /**
+     * Returns the time in microseconds that WiFi has been running in the given state.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiStateTime(@WifiState int wifiState,
+            long elapsedRealtimeUs, @StatName int which);
+
+    /**
+     * Returns the number of times that WiFi has entered the given state.
+     *
+     * {@hide}
+     */
+    public abstract int getWifiStateCount(@WifiState int wifiState, @StatName int which);
+
+    /**
+     * Returns the {@link Timer} object that tracks the given WiFi state.
+     *
+     * {@hide}
+     */
+    public abstract Timer getWifiStateTimer(@WifiState int wifiState);
+
+    /**
+     * Returns the time in microseconds that the wifi supplicant has been
+     * in a given state.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiSupplStateTime(@WifiSupplState int state, long elapsedRealtimeUs,
+            @StatName int which);
+
+    /**
+     * Returns the number of times that the wifi supplicant has transitioned
+     * to a given state.
+     *
+     * {@hide}
+     */
+    public abstract int getWifiSupplStateCount(@WifiSupplState int state, @StatName int which);
+
+    /**
+     * Returns the {@link Timer} object that tracks the given wifi supplicant state.
+     *
+     * {@hide}
+     */
+    public abstract Timer getWifiSupplStateTimer(@WifiSupplState int state);
+
+    public static final int NUM_WIFI_SIGNAL_STRENGTH_BINS = 5;
+
+    /**
+     * Returns the time in microseconds that WIFI has been running with
+     * the given signal strength.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiSignalStrengthTime(int strengthBin,
+            long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times WIFI has entered the given signal strength.
+     *
+     * {@hide}
+     */
+    public abstract int getWifiSignalStrengthCount(int strengthBin, int which);
+
+    /**
+     * Returns the {@link Timer} object that tracks the given WIFI signal strength.
+     *
+     * {@hide}
+     */
+    public abstract Timer getWifiSignalStrengthTimer(int strengthBin);
+
+    /**
+     * Returns the time in microseconds that the flashlight has been on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getFlashlightOnTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the flashlight has been turned on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getFlashlightOnCount(int which);
+
+    /**
+     * Returns the time in microseconds that the camera has been on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getCameraOnTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the time in microseconds that bluetooth scans were running while the device was
+     * on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getBluetoothScanTime(long elapsedRealtimeUs, int which);
+
+    public static final int NETWORK_MOBILE_RX_DATA = 0;
+    public static final int NETWORK_MOBILE_TX_DATA = 1;
+    public static final int NETWORK_WIFI_RX_DATA = 2;
+    public static final int NETWORK_WIFI_TX_DATA = 3;
+    public static final int NETWORK_BT_RX_DATA = 4;
+    public static final int NETWORK_BT_TX_DATA = 5;
+    public static final int NETWORK_MOBILE_BG_RX_DATA = 6;
+    public static final int NETWORK_MOBILE_BG_TX_DATA = 7;
+    public static final int NETWORK_WIFI_BG_RX_DATA = 8;
+    public static final int NETWORK_WIFI_BG_TX_DATA = 9;
+    public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_WIFI_BG_TX_DATA + 1;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public abstract long getNetworkActivityBytes(int type, int which);
+    public abstract long getNetworkActivityPackets(int type, int which);
+
+    /**
+     * Returns true if the BatteryStats object has detailed WiFi power reports.
+     * When true, calling {@link #getWifiControllerActivity()} will yield the
+     * actual power data.
+     */
+    public abstract boolean hasWifiActivityReporting();
+
+    /**
+     * Returns a {@link ControllerActivityCounter} which is an aggregate of the times spent
+     * in various radio controller states, such as transmit, receive, and idle.
+     * @return non-null {@link ControllerActivityCounter}
+     */
+    public abstract ControllerActivityCounter getWifiControllerActivity();
+
+    /**
+     * Returns true if the BatteryStats object has detailed bluetooth power reports.
+     * When true, calling {@link #getBluetoothControllerActivity()} will yield the
+     * actual power data.
+     */
+    public abstract boolean hasBluetoothActivityReporting();
+
+    /**
+     * Returns a {@link ControllerActivityCounter} which is an aggregate of the times spent
+     * in various radio controller states, such as transmit, receive, and idle.
+     * @return non-null {@link ControllerActivityCounter}
+     */
+    public abstract ControllerActivityCounter getBluetoothControllerActivity();
+
+    /**
+     * Returns true if the BatteryStats object has detailed modem power reports.
+     * When true, calling {@link #getModemControllerActivity()} will yield the
+     * actual power data.
+     */
+    public abstract boolean hasModemActivityReporting();
+
+    /**
+     * Returns a {@link ControllerActivityCounter} which is an aggregate of the times spent
+     * in various radio controller states, such as transmit, receive, and idle.
+     * @return non-null {@link ControllerActivityCounter}
+     */
+    public abstract ControllerActivityCounter getModemControllerActivity();
+
+    /**
+     * Return the wall clock time when battery stats data collection started.
+     */
+    public abstract long getStartClockTime();
+
+    /**
+     * Return platform version tag that we were running in when the battery stats started.
+     */
+    public abstract String getStartPlatformVersion();
+
+    /**
+     * Return platform version tag that we were running in when the battery stats ended.
+     */
+    public abstract String getEndPlatformVersion();
+
+    /**
+     * Return the internal version code of the parcelled format.
+     */
+    public abstract int getParcelVersion();
+
+    /**
+     * Return whether we are currently running on battery.
+     */
+    public abstract boolean getIsOnBattery();
+
+    /**
+     * Returns the timestamp of when battery stats collection started, in microseconds.
+     */
+    public abstract long getStatsStartRealtime();
+
+    /**
+     * Returns a SparseArray containing the statistics for each uid.
+     */
+    @UnsupportedAppUsage
+    public abstract SparseArray<? extends Uid> getUidStats();
+
+    /**
+     * Returns the current battery uptime in microseconds.
+     *
+     * @param curTime the amount of elapsed realtime in microseconds.
+     */
+    @UnsupportedAppUsage
+    public abstract long getBatteryUptime(long curTime);
+
+    /**
+     * Returns the current battery realtime in microseconds.
+     *
+     * @param curTime the amount of elapsed realtime in microseconds.
+     */
+    public abstract long getBatteryRealtime(long curTime);
+
+    /**
+     * Returns the battery percentage level at the last time the device was unplugged from power, or
+     * the last time it booted on battery power.
+     */
+    public abstract int getDischargeStartLevel();
+
+    /**
+     * Returns the current battery percentage level if we are in a discharge cycle, otherwise
+     * returns the level at the last plug event.
+     */
+    public abstract int getDischargeCurrentLevel();
+
+    /**
+     * Get the amount the battery has discharged since the stats were
+     * last reset after charging, as a lower-end approximation.
+     */
+    public abstract int getLowDischargeAmountSinceCharge();
+
+    /**
+     * Get the amount the battery has discharged since the stats were
+     * last reset after charging, as an upper-end approximation.
+     */
+    public abstract int getHighDischargeAmountSinceCharge();
+
+    /**
+     * Retrieve the discharge amount over the selected discharge period <var>which</var>.
+     */
+    public abstract int getDischargeAmount(int which);
+
+    /**
+     * Get the amount the battery has discharged while the screen was on,
+     * since the last time power was unplugged.
+     */
+    public abstract int getDischargeAmountScreenOn();
+
+    /**
+     * Get the amount the battery has discharged while the screen was on,
+     * since the last time the device was charged.
+     */
+    public abstract int getDischargeAmountScreenOnSinceCharge();
+
+    /**
+     * Get the amount the battery has discharged while the screen was off,
+     * since the last time power was unplugged.
+     */
+    public abstract int getDischargeAmountScreenOff();
+
+    /**
+     * Get the amount the battery has discharged while the screen was off,
+     * since the last time the device was charged.
+     */
+    public abstract int getDischargeAmountScreenOffSinceCharge();
+
+    /**
+     * Get the amount the battery has discharged while the screen was dozing,
+     * since the last time power was unplugged.
+     */
+    public abstract int getDischargeAmountScreenDoze();
+
+    /**
+     * Get the amount the battery has discharged while the screen was dozing,
+     * since the last time the device was charged.
+     */
+    public abstract int getDischargeAmountScreenDozeSinceCharge();
+
+    /**
+     * Returns the approximate CPU time (in microseconds) spent by the system server handling
+     * incoming service calls from apps.  The result is returned as an array of longs,
+     * organized as a sequence like this:
+     * <pre>
+     *     cluster1-speeed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
+     * </pre>
+     *
+     * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
+     * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
+     */
+    @Nullable
+    public abstract long[] getSystemServiceTimeAtCpuSpeeds();
+
+    /**
+     * Returns the total, last, or current battery uptime in microseconds.
+     *
+     * @param curTime the elapsed realtime in microseconds.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     */
+    @UnsupportedAppUsage
+    public abstract long computeBatteryUptime(long curTime, int which);
+
+    /**
+     * Returns the total, last, or current battery realtime in microseconds.
+     *
+     * @param curTime the current elapsed realtime in microseconds.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     */
+    @UnsupportedAppUsage
+    public abstract long computeBatteryRealtime(long curTime, int which);
+
+    /**
+     * Returns the total, last, or current battery screen off/doze uptime in microseconds.
+     *
+     * @param curTime the elapsed realtime in microseconds.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     */
+    public abstract long computeBatteryScreenOffUptime(long curTime, int which);
+
+    /**
+     * Returns the total, last, or current battery screen off/doze realtime in microseconds.
+     *
+     * @param curTime the current elapsed realtime in microseconds.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     */
+    public abstract long computeBatteryScreenOffRealtime(long curTime, int which);
+
+    /**
+     * Returns the total, last, or current uptime in microseconds.
+     *
+     * @param curTime the current elapsed realtime in microseconds.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     */
+    public abstract long computeUptime(long curTime, int which);
+
+    /**
+     * Returns the total, last, or current realtime in microseconds.
+     *
+     * @param curTime the current elapsed realtime in microseconds.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     */
+    public abstract long computeRealtime(long curTime, int which);
+
+    /**
+     * Compute an approximation for how much run time (in microseconds) is remaining on
+     * the battery.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently
+     * charging.
+     *
+     * @param curTime The current elapsed realtime in microseconds.
+     */
+    @UnsupportedAppUsage
+    public abstract long computeBatteryTimeRemaining(long curTime);
+
+    // The part of a step duration that is the actual time.
+    public static final long STEP_LEVEL_TIME_MASK = 0x000000ffffffffffL;
+
+    // Bits in a step duration that are the new battery level we are at.
+    public static final long STEP_LEVEL_LEVEL_MASK = 0x0000ff0000000000L;
+    public static final int STEP_LEVEL_LEVEL_SHIFT = 40;
+
+    // Bits in a step duration that are the initial mode we were in at that step.
+    public static final long STEP_LEVEL_INITIAL_MODE_MASK = 0x00ff000000000000L;
+    public static final int STEP_LEVEL_INITIAL_MODE_SHIFT = 48;
+
+    // Bits in a step duration that indicate which modes changed during that step.
+    public static final long STEP_LEVEL_MODIFIED_MODE_MASK = 0xff00000000000000L;
+    public static final int STEP_LEVEL_MODIFIED_MODE_SHIFT = 56;
+
+    // Step duration mode: the screen is on, off, dozed, etc; value is Display.STATE_* - 1.
+    public static final int STEP_LEVEL_MODE_SCREEN_STATE = 0x03;
+
+    // The largest value for screen state that is tracked in battery states. Any values above
+    // this should be mapped back to one of the tracked values before being tracked here.
+    public static final int MAX_TRACKED_SCREEN_STATE = Display.STATE_DOZE_SUSPEND;
+
+    // Step duration mode: power save is on.
+    public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
+
+    // Step duration mode: device is currently in idle mode.
+    public static final int STEP_LEVEL_MODE_DEVICE_IDLE = 0x08;
+
+    public static final int[] STEP_LEVEL_MODES_OF_INTEREST = new int[] {
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
+    };
+    public static final int[] STEP_LEVEL_MODE_VALUES = new int[] {
+            (Display.STATE_OFF-1),
+            (Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_OFF-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
+            (Display.STATE_ON-1),
+            (Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_DOZE-1),
+            (Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_DOZE_SUSPEND-1),
+            (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
+    };
+    public static final String[] STEP_LEVEL_MODE_LABELS = new String[] {
+            "screen off",
+            "screen off power save",
+            "screen off device idle",
+            "screen on",
+            "screen on power save",
+            "screen doze",
+            "screen doze power save",
+            "screen doze-suspend",
+            "screen doze-suspend power save",
+            "screen doze-suspend device idle",
+    };
+
+    /**
+     * Return the amount of battery discharge while the screen was off, measured in
+     * micro-Ampere-hours. This will be non-zero only if the device's battery has
+     * a coulomb counter.
+     */
+    public abstract long getUahDischargeScreenOff(int which);
+
+    /**
+     * Return the amount of battery discharge while the screen was in doze mode, measured in
+     * micro-Ampere-hours. This will be non-zero only if the device's battery has
+     * a coulomb counter.
+     */
+    public abstract long getUahDischargeScreenDoze(int which);
+
+    /**
+     * Return the amount of battery discharge  measured in micro-Ampere-hours. This will be
+     * non-zero only if the device's battery has a coulomb counter.
+     */
+    public abstract long getUahDischarge(int which);
+
+    /**
+     * @return the amount of battery discharge while the device is in light idle mode, measured in
+     * micro-Ampere-hours.
+     */
+    public abstract long getUahDischargeLightDoze(int which);
+
+    /**
+     * @return the amount of battery discharge while the device is in deep idle mode, measured in
+     * micro-Ampere-hours.
+     */
+    public abstract long getUahDischargeDeepDoze(int which);
+
+    /**
+     * Returns the estimated real battery capacity, which may be less than the capacity
+     * declared by the PowerProfile.
+     * @return The estimated battery capacity in mAh.
+     */
+    public abstract int getEstimatedBatteryCapacity();
+
+    /**
+     * @return The minimum learned battery capacity in uAh.
+     */
+    public abstract int getMinLearnedBatteryCapacity();
+
+    /**
+     * @return The maximum learned battery capacity in uAh.
+     */
+    public abstract int getMaxLearnedBatteryCapacity() ;
+
+    /**
+     * @return The latest learned battery capacity in uAh.
+     */
+    public abstract int getLearnedBatteryCapacity();
+
+    /**
+     * Return the array of discharge step durations.
+     */
+    public abstract LevelStepTracker getDischargeLevelStepTracker();
+
+    /**
+     * Return the array of daily discharge step durations.
+     */
+    public abstract LevelStepTracker getDailyDischargeLevelStepTracker();
+
+    /**
+     * Compute an approximation for how much time (in microseconds) remains until the battery
+     * is fully charged.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently
+     * discharging.
+     *
+     * @param curTime The current elepsed realtime in microseconds.
+     */
+    @UnsupportedAppUsage
+    public abstract long computeChargeTimeRemaining(long curTime);
+
+    /**
+     * Return the array of charge step durations.
+     */
+    public abstract LevelStepTracker getChargeLevelStepTracker();
+
+    /**
+     * Return the array of daily charge step durations.
+     */
+    public abstract LevelStepTracker getDailyChargeLevelStepTracker();
+
+    public abstract ArrayList<PackageChange> getDailyPackageChanges();
+
+    public abstract Map<String, ? extends Timer> getWakeupReasonStats();
+
+    public abstract Map<String, ? extends Timer> getKernelWakelockStats();
+
+    /**
+     * Returns aggregated wake lock stats.
+     */
+    public abstract WakeLockStats getWakeLockStats();
+
+    /**
+     * Returns aggregated Bluetooth stats.
+     */
+    public abstract BluetoothBatteryStats getBluetoothBatteryStats();
+
+    /**
+     * 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();
+
+    private final static void formatTimeRaw(StringBuilder out, long seconds) {
+        long days = seconds / (60 * 60 * 24);
+        if (days != 0) {
+            out.append(days);
+            out.append("d ");
+        }
+        long used = days * 60 * 60 * 24;
+
+        long hours = (seconds - used) / (60 * 60);
+        if (hours != 0 || used != 0) {
+            out.append(hours);
+            out.append("h ");
+        }
+        used += hours * 60 * 60;
+
+        long mins = (seconds-used) / 60;
+        if (mins != 0 || used != 0) {
+            out.append(mins);
+            out.append("m ");
+        }
+        used += mins * 60;
+
+        if (seconds != 0 || used != 0) {
+            out.append(seconds-used);
+            out.append("s ");
+        }
+    }
+
+    public final static void formatTimeMs(StringBuilder sb, long time) {
+        long sec = time / 1000;
+        formatTimeRaw(sb, sec);
+        sb.append(time - (sec * 1000));
+        sb.append("ms ");
+    }
+
+    public final static void formatTimeMsNoSpace(StringBuilder sb, long time) {
+        long sec = time / 1000;
+        formatTimeRaw(sb, sec);
+        sb.append(time - (sec * 1000));
+        sb.append("ms");
+    }
+
+    public final String formatRatioLocked(long num, long den) {
+        if (den == 0L) {
+            return "--%";
+        }
+        float perc = ((float)num) / ((float)den) * 100;
+        mFormatBuilder.setLength(0);
+        mFormatter.format("%.1f%%", perc);
+        return mFormatBuilder.toString();
+    }
+
+    final String formatBytesLocked(long bytes) {
+        mFormatBuilder.setLength(0);
+
+        if (bytes < BYTES_PER_KB) {
+            return bytes + "B";
+        } else if (bytes < BYTES_PER_MB) {
+            mFormatter.format("%.2fKB", bytes / (double) BYTES_PER_KB);
+            return mFormatBuilder.toString();
+        } else if (bytes < BYTES_PER_GB){
+            mFormatter.format("%.2fMB", bytes / (double) BYTES_PER_MB);
+            return mFormatBuilder.toString();
+        } else {
+            mFormatter.format("%.2fGB", bytes / (double) BYTES_PER_GB);
+            return mFormatBuilder.toString();
+        }
+    }
+
+    /**
+     * Converts charge in mAh to string.
+     */
+    public static String formatCharge(double power) {
+        return formatValue(power);
+    }
+
+    /**
+     * Converts double to string, limiting small values to 3 significant figures.
+     */
+    private static String formatValue(double value) {
+        if (value == 0) return "0";
+
+        final String format;
+        if (value < .00001) {
+            format = "%.8f";
+        } else if (value < .0001) {
+            format = "%.7f";
+        } else if (value < .001) {
+            format = "%.6f";
+        } else if (value < .01) {
+            format = "%.5f";
+        } else if (value < .1) {
+            format = "%.4f";
+        } else if (value < 1) {
+            format = "%.3f";
+        } else if (value < 10) {
+            format = "%.2f";
+        } else if (value < 100) {
+            format = "%.1f";
+        } else {
+            format = "%.0f";
+        }
+
+        // Use English locale because this is never used in UI (only in checkin and dump).
+        return String.format(Locale.ENGLISH, format, value);
+    }
+
+    private static long roundUsToMs(long timeUs) {
+        return (timeUs + 500) / 1000;
+    }
+
+    private static long computeWakeLock(Timer timer, long elapsedRealtimeUs, int which) {
+        if (timer != null) {
+            // Convert from microseconds to milliseconds with rounding
+            long totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which);
+            long totalTimeMillis = (totalTimeMicros + 500) / 1000;
+            return totalTimeMillis;
+        }
+        return 0;
+    }
+
+    /**
+     *
+     * @param sb a StringBuilder object.
+     * @param timer a Timer object contining the wakelock times.
+     * @param elapsedRealtimeUs the current on-battery time in microseconds.
+     * @param name the name of the wakelock.
+     * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     * @param linePrefix a String to be prepended to each line of output.
+     * @return the line prefix
+     */
+    private static final String printWakeLock(StringBuilder sb, Timer timer,
+            long elapsedRealtimeUs, String name, int which, String linePrefix) {
+
+        if (timer != null) {
+            long totalTimeMillis = computeWakeLock(timer, elapsedRealtimeUs, which);
+
+            int count = timer.getCountLocked(which);
+            if (totalTimeMillis != 0) {
+                sb.append(linePrefix);
+                formatTimeMs(sb, totalTimeMillis);
+                if (name != null) {
+                    sb.append(name);
+                    sb.append(' ');
+                }
+                sb.append('(');
+                sb.append(count);
+                sb.append(" times)");
+                final long maxDurationMs = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
+                if (maxDurationMs >= 0) {
+                    sb.append(" max=");
+                    sb.append(maxDurationMs);
+                }
+                // Put actual time if it is available and different from totalTimeMillis.
+                final long totalDurMs = timer.getTotalDurationMsLocked(elapsedRealtimeUs/1000);
+                if (totalDurMs > totalTimeMillis) {
+                    sb.append(" actual=");
+                    sb.append(totalDurMs);
+                }
+                if (timer.isRunningLocked()) {
+                    final long currentMs = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
+                    if (currentMs >= 0) {
+                        sb.append(" (running for ");
+                        sb.append(currentMs);
+                        sb.append("ms)");
+                    } else {
+                        sb.append(" (running)");
+                    }
+                }
+
+                return ", ";
+            }
+        }
+        return linePrefix;
+    }
+
+    /**
+     * Prints details about a timer, if its total time was greater than 0.
+     *
+     * @param pw a PrintWriter object to print to.
+     * @param sb a StringBuilder object.
+     * @param timer a Timer object contining the wakelock times.
+     * @param rawRealtimeUs the current on-battery time in microseconds.
+     * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     * @param prefix a String to be prepended to each line of output.
+     * @param type the name of the timer.
+     * @return true if anything was printed.
+     */
+    private static final boolean printTimer(PrintWriter pw, StringBuilder sb, Timer timer,
+            long rawRealtimeUs, int which, String prefix, String type) {
+        if (timer != null) {
+            // Convert from microseconds to milliseconds with rounding
+            final long totalTimeMs = (timer.getTotalTimeLocked(
+                    rawRealtimeUs, which) + 500) / 1000;
+            final int count = timer.getCountLocked(which);
+            if (totalTimeMs != 0) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    ");
+                sb.append(type);
+                sb.append(": ");
+                formatTimeMs(sb, totalTimeMs);
+                sb.append("realtime (");
+                sb.append(count);
+                sb.append(" times)");
+                final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs/1000);
+                if (maxDurationMs >= 0) {
+                    sb.append(" max=");
+                    sb.append(maxDurationMs);
+                }
+                if (timer.isRunningLocked()) {
+                    final long currentMs = timer.getCurrentDurationMsLocked(rawRealtimeUs/1000);
+                    if (currentMs >= 0) {
+                        sb.append(" (running for ");
+                        sb.append(currentMs);
+                        sb.append("ms)");
+                    } else {
+                        sb.append(" (running)");
+                    }
+                }
+                pw.println(sb.toString());
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checkin version of wakelock printer. Prints simple comma-separated list.
+     *
+     * @param sb a StringBuilder object.
+     * @param timer a Timer object contining the wakelock times.
+     * @param elapsedRealtimeUs the current time in microseconds.
+     * @param name the name of the wakelock.
+     * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     * @param linePrefix a String to be prepended to each line of output.
+     * @return the line prefix
+     */
+    private static final String printWakeLockCheckin(StringBuilder sb, Timer timer,
+            long elapsedRealtimeUs, String name, int which, String linePrefix) {
+        long totalTimeMicros = 0;
+        int count = 0;
+        long max = 0;
+        long current = 0;
+        long totalDuration = 0;
+        if (timer != null) {
+            totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which);
+            count = timer.getCountLocked(which);
+            current = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
+            max = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
+            totalDuration = timer.getTotalDurationMsLocked(elapsedRealtimeUs/1000);
+        }
+        sb.append(linePrefix);
+        sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding
+        sb.append(',');
+        sb.append(name != null ? name + "," : "");
+        sb.append(count);
+        sb.append(',');
+        sb.append(current);
+        sb.append(',');
+        sb.append(max);
+        // Partial, full, and window wakelocks are pooled, so totalDuration is meaningful (albeit
+        // not always tracked). Kernel wakelocks (which have name == null) have no notion of
+        // totalDuration independent of totalTimeMicros (since they are not pooled).
+        if (name != null) {
+            sb.append(',');
+            sb.append(totalDuration);
+        }
+        return ",";
+    }
+
+    private static final void dumpLineHeader(PrintWriter pw, int uid, String category,
+                                             String type) {
+        pw.print(BATTERY_STATS_CHECKIN_VERSION);
+        pw.print(',');
+        pw.print(uid);
+        pw.print(',');
+        pw.print(category);
+        pw.print(',');
+        pw.print(type);
+    }
+
+    /**
+     * Dump a comma-separated line of values for terse checkin mode.
+     *
+     * @param pw the PageWriter to dump log to
+     * @param category category of data (e.g. "total", "last", "unplugged", "current" )
+     * @param type type of data (e.g. "wakelock", "sensor", "process", "apk" ,  "process", "network")
+     * @param args type-dependent data arguments
+     */
+    @UnsupportedAppUsage
+    private static final void dumpLine(PrintWriter pw, int uid, String category, String type,
+           Object... args ) {
+        dumpLineHeader(pw, uid, category, type);
+        for (Object arg : args) {
+            pw.print(',');
+            pw.print(arg);
+        }
+        pw.println();
+    }
+
+    /**
+     * Dump a given timer stat for terse checkin mode.
+     *
+     * @param pw the PageWriter to dump log to
+     * @param uid the UID to log
+     * @param category category of data (e.g. "total", "last", "unplugged", "current" )
+     * @param type type of data (e.g. "wakelock", "sensor", "process", "apk" ,  "process", "network")
+     * @param timer a {@link Timer} to dump stats for
+     * @param rawRealtime the current elapsed realtime of the system in microseconds
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+     */
+    private static final void dumpTimer(PrintWriter pw, int uid, String category, String type,
+                                        Timer timer, long rawRealtime, int which) {
+        if (timer != null) {
+            // Convert from microseconds to milliseconds with rounding
+            final long totalTime = roundUsToMs(timer.getTotalTimeLocked(rawRealtime, which));
+            final int count = timer.getCountLocked(which);
+            if (totalTime != 0 || count != 0) {
+                dumpLine(pw, uid, category, type, totalTime, count);
+            }
+        }
+    }
+
+    /**
+     * Dump a given timer stat to the proto stream.
+     *
+     * @param proto the ProtoOutputStream to log to
+     * @param fieldId type of data, the field to save to (e.g. AggregatedBatteryStats.WAKELOCK)
+     * @param timer a {@link Timer} to dump stats for
+     * @param rawRealtimeUs the current elapsed realtime of the system in microseconds
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+     */
+    private static void dumpTimer(ProtoOutputStream proto, long fieldId,
+                                        Timer timer, long rawRealtimeUs, int which) {
+        if (timer == null) {
+            return;
+        }
+        // Convert from microseconds to milliseconds with rounding
+        final long timeMs = roundUsToMs(timer.getTotalTimeLocked(rawRealtimeUs, which));
+        final int count = timer.getCountLocked(which);
+        final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs / 1000);
+        final long curDurationMs = timer.getCurrentDurationMsLocked(rawRealtimeUs / 1000);
+        final long totalDurationMs = timer.getTotalDurationMsLocked(rawRealtimeUs / 1000);
+        if (timeMs != 0 || count != 0 || maxDurationMs != -1 || curDurationMs != -1
+                || totalDurationMs != -1) {
+            final long token = proto.start(fieldId);
+            proto.write(TimerProto.DURATION_MS, timeMs);
+            proto.write(TimerProto.COUNT, count);
+            // These values will be -1 for timers that don't implement the functionality.
+            if (maxDurationMs != -1) {
+                proto.write(TimerProto.MAX_DURATION_MS, maxDurationMs);
+            }
+            if (curDurationMs != -1) {
+                proto.write(TimerProto.CURRENT_DURATION_MS, curDurationMs);
+            }
+            if (totalDurationMs != -1) {
+                proto.write(TimerProto.TOTAL_DURATION_MS, totalDurationMs);
+            }
+            proto.end(token);
+        }
+    }
+
+    /**
+     * Checks if the ControllerActivityCounter has any data worth dumping.
+     */
+    private static boolean controllerActivityHasData(ControllerActivityCounter counter, int which) {
+        if (counter == null) {
+            return false;
+        }
+
+        if (counter.getIdleTimeCounter().getCountLocked(which) != 0
+                || counter.getRxTimeCounter().getCountLocked(which) != 0
+                || counter.getPowerCounter().getCountLocked(which) != 0
+                || counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) != 0) {
+            return true;
+        }
+
+        for (LongCounter c : counter.getTxTimeCounters()) {
+            if (c.getCountLocked(which) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Dumps the ControllerActivityCounter if it has any data worth dumping.
+     * The order of the arguments in the final check in line is:
+     *
+     * idle, rx, power, tx...
+     *
+     * where tx... is one or more transmit level times.
+     */
+    private static final void dumpControllerActivityLine(PrintWriter pw, int uid, String category,
+                                                         String type,
+                                                         ControllerActivityCounter counter,
+                                                         int which) {
+        if (!controllerActivityHasData(counter, which)) {
+            return;
+        }
+
+        dumpLineHeader(pw, uid, category, type);
+        pw.print(",");
+        pw.print(counter.getIdleTimeCounter().getCountLocked(which));
+        pw.print(",");
+        pw.print(counter.getRxTimeCounter().getCountLocked(which));
+        pw.print(",");
+        pw.print(counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+        pw.print(",");
+        pw.print(counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+                / (MILLISECONDS_IN_HOUR));
+        for (LongCounter c : counter.getTxTimeCounters()) {
+            pw.print(",");
+            pw.print(c.getCountLocked(which));
+        }
+        pw.println();
+    }
+
+    /**
+     * Dumps the ControllerActivityCounter if it has any data worth dumping.
+     */
+    private static void dumpControllerActivityProto(ProtoOutputStream proto, long fieldId,
+                                                    ControllerActivityCounter counter,
+                                                    int which) {
+        if (!controllerActivityHasData(counter, which)) {
+            return;
+        }
+
+        final long cToken = proto.start(fieldId);
+
+        proto.write(ControllerActivityProto.IDLE_DURATION_MS,
+                counter.getIdleTimeCounter().getCountLocked(which));
+        proto.write(ControllerActivityProto.RX_DURATION_MS,
+                counter.getRxTimeCounter().getCountLocked(which));
+        proto.write(ControllerActivityProto.POWER_MAH,
+                counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+        proto.write(ControllerActivityProto.MONITORED_RAIL_CHARGE_MAH,
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+                        / (MILLISECONDS_IN_HOUR));
+
+        long tToken;
+        LongCounter[] txCounters = counter.getTxTimeCounters();
+        for (int i = 0; i < txCounters.length; ++i) {
+            LongCounter c = txCounters[i];
+            tToken = proto.start(ControllerActivityProto.TX);
+            proto.write(ControllerActivityProto.TxLevel.LEVEL, i);
+            proto.write(ControllerActivityProto.TxLevel.DURATION_MS, c.getCountLocked(which));
+            proto.end(tToken);
+        }
+
+        proto.end(cToken);
+    }
+
+    private final void printControllerActivityIfInteresting(PrintWriter pw, StringBuilder sb,
+                                                            String prefix, String controllerName,
+                                                            ControllerActivityCounter counter,
+                                                            int which) {
+        if (controllerActivityHasData(counter, which)) {
+            printControllerActivity(pw, sb, prefix, controllerName, counter, which);
+        }
+    }
+
+    private final void printControllerActivity(PrintWriter pw, StringBuilder sb, String prefix,
+                                               String controllerName,
+                                               ControllerActivityCounter counter, int which) {
+        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+        final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which);
+        final long monitoredRailChargeConsumedMaMs =
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
+        // Battery real time
+        final long totalControllerActivityTimeMs
+            = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
+        long totalTxTimeMs = 0;
+        for (LongCounter txState : counter.getTxTimeCounters()) {
+            totalTxTimeMs += txState.getCountLocked(which);
+        }
+
+        if (controllerName.equals(WIFI_CONTROLLER_NAME)) {
+            final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Scan time:  ");
+            formatTimeMs(sb, scanTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+
+            final long sleepTimeMs
+                = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Sleep time:  ");
+            formatTimeMs(sb, sleepTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
+        if (controllerName.equals(CELLULAR_CONTROLLER_NAME)) {
+            final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Sleep time:  ");
+            formatTimeMs(sb, sleepTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     ");
+        sb.append(controllerName);
+        sb.append(" Idle time:   ");
+        formatTimeMs(sb, idleTimeMs);
+        sb.append("(");
+        sb.append(formatRatioLocked(idleTimeMs, totalControllerActivityTimeMs));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     ");
+        sb.append(controllerName);
+        sb.append(" Rx time:     ");
+        formatTimeMs(sb, rxTimeMs);
+        sb.append("(");
+        sb.append(formatRatioLocked(rxTimeMs, totalControllerActivityTimeMs));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     ");
+        sb.append(controllerName);
+        sb.append(" Tx time:     ");
+
+        String [] powerLevel;
+        switch(controllerName) {
+            case CELLULAR_CONTROLLER_NAME:
+                powerLevel = new String[] {
+                    "   less than 0dBm: ",
+                    "   0dBm to 8dBm: ",
+                    "   8dBm to 15dBm: ",
+                    "   15dBm to 20dBm: ",
+                    "   above 20dBm: "};
+                break;
+            default:
+                powerLevel = new String[] {"[0]", "[1]", "[2]", "[3]", "[4]"};
+                break;
+        }
+        final int numTxLvls = Math.min(counter.getTxTimeCounters().length, powerLevel.length);
+        if (numTxLvls > 1) {
+            pw.println(sb.toString());
+            for (int lvl = 0; lvl < numTxLvls; lvl++) {
+                final long txLvlTimeMs = counter.getTxTimeCounters()[lvl].getCountLocked(which);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    ");
+                sb.append(powerLevel[lvl]);
+                sb.append(" ");
+                formatTimeMs(sb, txLvlTimeMs);
+                sb.append("(");
+                sb.append(formatRatioLocked(txLvlTimeMs, totalControllerActivityTimeMs));
+                sb.append(")");
+                pw.println(sb.toString());
+            }
+        } else {
+            final long txLvlTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
+            formatTimeMs(sb, txLvlTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(txLvlTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
+        if (powerDrainMaMs > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Battery drain: ").append(
+                    formatCharge(powerDrainMaMs / MILLISECONDS_IN_HOUR));
+            sb.append("mAh");
+            pw.println(sb.toString());
+        }
+
+        if (monitoredRailChargeConsumedMaMs > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Monitored rail energy drain: ").append(
+                    new DecimalFormat("#.##").format(
+                            monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+    }
+
+    private void printCellularPerRatBreakdown(PrintWriter pw, StringBuilder sb, String prefix,
+            long rawRealtimeMs) {
+        final String allFrequenciesHeader =
+                "    All frequencies:\n";
+        final String[] nrFrequencyRangeDescription = new String[]{
+                "    Unknown frequency:\n",
+                "    Low frequency (less than 1GHz):\n",
+                "    Middle frequency (1GHz to 3GHz):\n",
+                "    High frequency (3GHz to 6GHz):\n",
+                "    Mmwave frequency (greater than 6GHz):\n"};
+        final String signalStrengthHeader =
+                "      Signal Strength Time:\n";
+        final String txHeader =
+                "      Tx Time:\n";
+        final String rxHeader =
+                "      Rx Time: ";
+        final String[] signalStrengthDescription = new String[]{
+                "        unknown:  ",
+                "        poor:     ",
+                "        moderate: ",
+                "        good:     ",
+                "        great:    "};
+
+        final long totalActiveTimesMs = getMobileRadioActiveTime(rawRealtimeMs * 1000,
+                STATS_SINCE_CHARGED) / 1000;
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("Active Cellular Radio Access Technology Breakdown:");
+        pw.println(sb);
+
+        boolean hasData = false;
+        final int numSignalStrength = CellSignalStrength.getNumSignalStrengthLevels();
+        for (int rat = RADIO_ACCESS_TECHNOLOGY_COUNT - 1; rat >= 0; rat--) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  ");
+            sb.append(RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
+            sb.append(":\n");
+            sb.append(prefix);
+
+            final int numFreqLvl =
+                    rat == RADIO_ACCESS_TECHNOLOGY_NR ? nrFrequencyRangeDescription.length : 1;
+            for (int freqLvl = numFreqLvl - 1; freqLvl >= 0; freqLvl--) {
+                final int freqDescriptionStart = sb.length();
+                boolean hasFreqData = false;
+                if (rat == RADIO_ACCESS_TECHNOLOGY_NR) {
+                    sb.append(nrFrequencyRangeDescription[freqLvl]);
+                } else {
+                    sb.append(allFrequenciesHeader);
+                }
+
+                sb.append(prefix);
+                sb.append(signalStrengthHeader);
+                for (int strength = 0; strength < numSignalStrength; strength++) {
+                    final long timeMs = getActiveRadioDurationMs(rat, freqLvl, strength,
+                            rawRealtimeMs);
+                    if (timeMs <= 0) continue;
+                    hasFreqData = true;
+                    sb.append(prefix);
+                    sb.append(signalStrengthDescription[strength]);
+                    formatTimeMs(sb, timeMs);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(timeMs, totalActiveTimesMs));
+                    sb.append(")\n");
+                }
+
+                sb.append(prefix);
+                sb.append(txHeader);
+                for (int strength = 0; strength < numSignalStrength; strength++) {
+                    final long timeMs = getActiveTxRadioDurationMs(rat, freqLvl, strength,
+                            rawRealtimeMs);
+                    if (timeMs <= 0) continue;
+                    hasFreqData = true;
+                    sb.append(prefix);
+                    sb.append(signalStrengthDescription[strength]);
+                    formatTimeMs(sb, timeMs);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(timeMs, totalActiveTimesMs));
+                    sb.append(")\n");
+                }
+
+                sb.append(prefix);
+                sb.append(rxHeader);
+                final long rxTimeMs = getActiveRxRadioDurationMs(rat, freqLvl, rawRealtimeMs);
+                formatTimeMs(sb, rxTimeMs);
+                sb.append("(");
+                sb.append(formatRatioLocked(rxTimeMs, totalActiveTimesMs));
+                sb.append(")\n");
+
+                if (hasFreqData) {
+                    hasData = true;
+                    pw.print(sb);
+                    sb.setLength(0);
+                    sb.append(prefix);
+                } else {
+                    // No useful data was printed, rewind sb to before the start of this frequency.
+                    sb.setLength(freqDescriptionStart);
+                }
+            }
+        }
+
+        if (!hasData) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  (no activity)");
+            pw.println(sb);
+        }
+    }
+
+    private static final String[] CHECKIN_POWER_COMPONENT_LABELS =
+            new String[BatteryConsumer.POWER_COMPONENT_COUNT];
+    static {
+        // Assign individually to avoid future mismatch of indices
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_SCREEN] = "scrn";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_CPU] = "cpu";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_BLUETOOTH] = "blue";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_CAMERA] = "camera";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_AUDIO] = "audio";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_VIDEO] = "video";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_FLASHLIGHT] = "flashlight";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO] = "cell";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_SENSORS] = "sensors";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_GNSS] = "gnss";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_WIFI] = "wifi";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_MEMORY] = "memory";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_PHONE] = "phone";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY] = "ambi";
+        CHECKIN_POWER_COMPONENT_LABELS[BatteryConsumer.POWER_COMPONENT_IDLE] = "idle";
+    }
+
+    /**
+     * Checkin server version of dump to produce more compact, computer-readable log.
+     *
+     * NOTE: all times are expressed in microseconds, unless specified otherwise.
+     */
+    public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid,
+            boolean wifiOnly) {
+
+        if (which != BatteryStats.STATS_SINCE_CHARGED) {
+            dumpLine(pw, 0, STAT_NAMES[which], "err",
+                    "ERROR: BatteryStats.dumpCheckin called for which type " + which
+                    + " but only STATS_SINCE_CHARGED is supported.");
+            return;
+        }
+
+        final long rawUptime = SystemClock.uptimeMillis() * 1000;
+        final long rawRealtimeMs = SystemClock.elapsedRealtime();
+        final long rawRealtime = rawRealtimeMs * 1000;
+        final long batteryUptime = getBatteryUptime(rawUptime);
+        final long whichBatteryUptime = computeBatteryUptime(rawUptime, which);
+        final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which);
+        final long whichBatteryScreenOffUptime = computeBatteryScreenOffUptime(rawUptime, which);
+        final long whichBatteryScreenOffRealtime = computeBatteryScreenOffRealtime(rawRealtime,
+                which);
+        final long totalRealtime = computeRealtime(rawRealtime, which);
+        final long totalUptime = computeUptime(rawUptime, which);
+        final long screenOnTime = getScreenOnTime(rawRealtime, which);
+        final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
+        final long interactiveTime = getInteractiveTime(rawRealtime, which);
+        final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
+        final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT,
+                rawRealtime, which);
+        final long deviceIdleModeFullTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP,
+                rawRealtime, which);
+        final long deviceLightIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT,
+                rawRealtime, which);
+        final long deviceIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_DEEP,
+                rawRealtime, which);
+        final int connChanges = getNumConnectivityChange(which);
+        final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
+        final long dischargeCount = getUahDischarge(which);
+        final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
+        final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
+        final long dischargeLightDozeCount = getUahDischargeLightDoze(which);
+        final long dischargeDeepDozeCount = getUahDischargeDeepDoze(which);
+
+        final StringBuilder sb = new StringBuilder(128);
+
+        final SparseArray<? extends Uid> uidStats = getUidStats();
+        final int NU = uidStats.size();
+
+        final String category = STAT_NAMES[which];
+
+        // Dump "battery" stat
+        dumpLine(pw, 0 /* uid */, category, BATTERY_DATA,
+                which == STATS_SINCE_CHARGED ? getStartCount() : "N/A",
+                whichBatteryRealtime / 1000, whichBatteryUptime / 1000,
+                totalRealtime / 1000, totalUptime / 1000,
+                getStartClockTime(),
+                whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
+                getEstimatedBatteryCapacity(),
+                getMinLearnedBatteryCapacity(),
+                getMaxLearnedBatteryCapacity(),
+                screenDozeTime / 1000);
+
+
+        // Calculate wakelock times across all uids.
+        long fullWakeLockTimeTotal = 0;
+        long partialWakeLockTimeTotal = 0;
+
+        for (int iu = 0; iu < NU; iu++) {
+            final Uid u = uidStats.valueAt(iu);
+
+            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+                    = u.getWakelockStats();
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+
+                final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+                if (fullWakeTimer != null) {
+                    fullWakeLockTimeTotal += fullWakeTimer.getTotalTimeLocked(rawRealtime,
+                            which);
+                }
+
+                final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (partialWakeTimer != null) {
+                    partialWakeLockTimeTotal += partialWakeTimer.getTotalTimeLocked(
+                        rawRealtime, which);
+                }
+            }
+        }
+
+        // Dump network stats
+        final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+        final long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+        final long btRxTotalBytes = getNetworkActivityBytes(NETWORK_BT_RX_DATA, which);
+        final long btTxTotalBytes = getNetworkActivityBytes(NETWORK_BT_TX_DATA, which);
+        dumpLine(pw, 0 /* uid */, category, GLOBAL_NETWORK_DATA,
+                mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
+                mobileRxTotalPackets, mobileTxTotalPackets, wifiRxTotalPackets, wifiTxTotalPackets,
+                btRxTotalBytes, btTxTotalBytes);
+
+        // Dump Modem controller stats
+        dumpControllerActivityLine(pw, 0 /* uid */, category, GLOBAL_MODEM_CONTROLLER_DATA,
+                getModemControllerActivity(), which);
+
+        // Dump Wifi controller stats
+        final long wifiOnTime = getWifiOnTime(rawRealtime, which);
+        final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
+        dumpLine(pw, 0 /* uid */, category, GLOBAL_WIFI_DATA, wifiOnTime / 1000,
+                wifiRunningTime / 1000, /* legacy fields follow, keep at 0 */ 0, 0, 0);
+
+        dumpControllerActivityLine(pw, 0 /* uid */, category, GLOBAL_WIFI_CONTROLLER_DATA,
+                getWifiControllerActivity(), which);
+
+        // Dump Bluetooth controller stats
+        dumpControllerActivityLine(pw, 0 /* uid */, category, GLOBAL_BLUETOOTH_CONTROLLER_DATA,
+                getBluetoothControllerActivity(), which);
+
+        // Dump misc stats
+        dumpLine(pw, 0 /* uid */, category, MISC_DATA,
+                screenOnTime / 1000, phoneOnTime / 1000,
+                fullWakeLockTimeTotal / 1000, partialWakeLockTimeTotal / 1000,
+                getMobileRadioActiveTime(rawRealtime, which) / 1000,
+                getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
+                powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeFullTime / 1000,
+                getDeviceIdleModeCount(DEVICE_IDLE_MODE_DEEP, which), deviceIdlingTime / 1000,
+                getDeviceIdlingCount(DEVICE_IDLE_MODE_DEEP, which),
+                getMobileRadioActiveCount(which),
+                getMobileRadioActiveUnknownTime(which) / 1000, deviceIdleModeLightTime / 1000,
+                getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which), deviceLightIdlingTime / 1000,
+                getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which),
+                getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT),
+                getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP));
+
+        // Dump screen brightness stats
+        Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
+        for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+            args[i] = getScreenBrightnessTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, SCREEN_BRIGHTNESS_DATA, args);
+
+        // Dump signal strength stats
+        args = new Object[CellSignalStrength.getNumSignalStrengthLevels()];
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+            args[i] = getPhoneSignalStrengthTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, SIGNAL_STRENGTH_TIME_DATA, args);
+        dumpLine(pw, 0 /* uid */, category, SIGNAL_SCANNING_TIME_DATA,
+                getPhoneSignalScanningTime(rawRealtime, which) / 1000);
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
+            args[i] = getPhoneSignalStrengthCount(i, which);
+        }
+        dumpLine(pw, 0 /* uid */, category, SIGNAL_STRENGTH_COUNT_DATA, args);
+
+        // Dump network type stats
+        args = new Object[NUM_DATA_CONNECTION_TYPES];
+        for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+            args[i] = getPhoneDataConnectionTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, DATA_CONNECTION_TIME_DATA, args);
+        for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+            args[i] = getPhoneDataConnectionCount(i, which);
+        }
+        dumpLine(pw, 0 /* uid */, category, DATA_CONNECTION_COUNT_DATA, args);
+
+        // Dump wifi state stats
+        args = new Object[NUM_WIFI_STATES];
+        for (int i=0; i<NUM_WIFI_STATES; i++) {
+            args[i] = getWifiStateTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_STATE_TIME_DATA, args);
+        for (int i=0; i<NUM_WIFI_STATES; i++) {
+            args[i] = getWifiStateCount(i, which);
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_STATE_COUNT_DATA, args);
+
+        // Dump wifi suppl state stats
+        args = new Object[NUM_WIFI_SUPPL_STATES];
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            args[i] = getWifiSupplStateTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SUPPL_STATE_TIME_DATA, args);
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            args[i] = getWifiSupplStateCount(i, which);
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SUPPL_STATE_COUNT_DATA, args);
+
+        // Dump wifi signal strength stats
+        args = new Object[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            args[i] = getWifiSignalStrengthTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_TIME_DATA, args);
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            args[i] = getWifiSignalStrengthCount(i, which);
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_COUNT_DATA, args);
+
+        // Dump Multicast total stats
+        final long multicastWakeLockTimeTotalMicros =
+                getWifiMulticastWakelockTime(rawRealtime, which);
+        final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which);
+        dumpLine(pw, 0 /* uid */, category, WIFI_MULTICAST_TOTAL_DATA,
+                multicastWakeLockTimeTotalMicros / 1000,
+                multicastWakeLockCountTotal);
+
+        dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA,
+                getLowDischargeAmountSinceCharge(), getHighDischargeAmountSinceCharge(),
+                getDischargeAmountScreenOnSinceCharge(),
+                getDischargeAmountScreenOffSinceCharge(),
+                dischargeCount / 1000, dischargeScreenOffCount / 1000,
+                getDischargeAmountScreenDozeSinceCharge(), dischargeScreenDozeCount / 1000,
+                dischargeLightDozeCount / 1000, dischargeDeepDozeCount / 1000);
+
+        if (reqUid < 0) {
+            final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
+                    sb.setLength(0);
+                    printWakeLockCheckin(sb, ent.getValue(), rawRealtime, null, which, "");
+                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA,
+                            "\"" + ent.getKey() + "\"", sb.toString());
+                }
+            }
+            final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+                    // Not doing the regular wake lock formatting to remain compatible
+                    // with the old checkin format.
+                    long totalTimeMicros = ent.getValue().getTotalTimeLocked(rawRealtime, which);
+                    int count = ent.getValue().getCountLocked(which);
+                    dumpLine(pw, 0 /* uid */, category, WAKEUP_REASON_DATA,
+                            "\"" + ent.getKey() + "\"", (totalTimeMicros + 500) / 1000, count);
+                }
+            }
+        }
+
+        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 BatteryUsageStats stats = getBatteryUsageStats(context, true /* detailed */);
+        dumpLine(pw, 0 /* uid */, category, POWER_USE_SUMMARY_DATA,
+                formatCharge(stats.getBatteryCapacity()),
+                formatCharge(stats.getConsumedPower()),
+                formatCharge(stats.getDischargedPowerRange().getLower()),
+                formatCharge(stats.getDischargedPowerRange().getUpper()));
+        final BatteryConsumer deviceConsumer = stats.getAggregateBatteryConsumer(
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+        for (@BatteryConsumer.PowerComponent int powerComponent = 0;
+                powerComponent < BatteryConsumer.POWER_COMPONENT_COUNT; powerComponent++) {
+            String label = CHECKIN_POWER_COMPONENT_LABELS[powerComponent];
+            if (label == null) {
+                label = "???";
+            }
+            dumpLine(pw, 0 /* uid */, category, POWER_USE_ITEM_DATA, label,
+                    formatCharge(deviceConsumer.getConsumedPower(powerComponent)),
+                    shouldHidePowerComponent(powerComponent) ? 1 : 0, "0", "0");
+        }
+
+        final ProportionalAttributionCalculator proportionalAttributionCalculator =
+                new ProportionalAttributionCalculator(context, stats);
+        final List<UidBatteryConsumer> uidBatteryConsumers = stats.getUidBatteryConsumers();
+        for (int i = 0; i < uidBatteryConsumers.size(); i++) {
+            UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
+            dumpLine(pw, consumer.getUid(), category, POWER_USE_ITEM_DATA, "uid",
+                    formatCharge(consumer.getConsumedPower()),
+                    proportionalAttributionCalculator.isSystemBatteryConsumer(consumer) ? 1 : 0,
+                    formatCharge(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)),
+                    formatCharge(
+                            proportionalAttributionCalculator.getProportionalPowerMah(consumer)));
+        }
+
+        final long[] cpuFreqs = getCpuFreqs();
+        if (cpuFreqs != null) {
+            sb.setLength(0);
+            for (int i = 0; i < cpuFreqs.length; ++i) {
+                if (i != 0) sb.append(',');
+                sb.append(cpuFreqs[i]);
+            }
+            dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
+        }
+
+        // Dump stats per UID.
+        for (int iu = 0; iu < NU; iu++) {
+            final int uid = uidStats.keyAt(iu);
+            if (reqUid >= 0 && uid != reqUid) {
+                continue;
+            }
+            final Uid u = uidStats.valueAt(iu);
+
+            // Dump Network stats per uid, if any
+            final long mobileBytesRx = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+            final long mobileBytesTx = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+            final long wifiBytesRx = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+            final long wifiBytesTx = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+            final long mobilePacketsRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+            final long mobilePacketsTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+            final long mobileActiveTime = u.getMobileRadioActiveTime(which);
+            final int mobileActiveCount = u.getMobileRadioActiveCount(which);
+            final long mobileWakeup = u.getMobileRadioApWakeupCount(which);
+            final long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+            final long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+            final long wifiWakeup = u.getWifiRadioApWakeupCount(which);
+            final long btBytesRx = u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which);
+            final long btBytesTx = u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which);
+            // Background data transfers
+            final long mobileBytesBgRx = u.getNetworkActivityBytes(NETWORK_MOBILE_BG_RX_DATA,
+                    which);
+            final long mobileBytesBgTx = u.getNetworkActivityBytes(NETWORK_MOBILE_BG_TX_DATA,
+                    which);
+            final long wifiBytesBgRx = u.getNetworkActivityBytes(NETWORK_WIFI_BG_RX_DATA, which);
+            final long wifiBytesBgTx = u.getNetworkActivityBytes(NETWORK_WIFI_BG_TX_DATA, which);
+            final long mobilePacketsBgRx = u.getNetworkActivityPackets(NETWORK_MOBILE_BG_RX_DATA,
+                    which);
+            final long mobilePacketsBgTx = u.getNetworkActivityPackets(NETWORK_MOBILE_BG_TX_DATA,
+                    which);
+            final long wifiPacketsBgRx = u.getNetworkActivityPackets(NETWORK_WIFI_BG_RX_DATA,
+                    which);
+            final long wifiPacketsBgTx = u.getNetworkActivityPackets(NETWORK_WIFI_BG_TX_DATA,
+                    which);
+
+            if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0
+                    || mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0
+                    || wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0
+                    || btBytesRx > 0 || btBytesTx > 0 || mobileWakeup > 0 || wifiWakeup > 0
+                    || mobileBytesBgRx > 0 || mobileBytesBgTx > 0 || wifiBytesBgRx > 0
+                    || wifiBytesBgTx > 0
+                    || mobilePacketsBgRx > 0 || mobilePacketsBgTx > 0 || wifiPacketsBgRx > 0
+                    || wifiPacketsBgTx > 0) {
+                dumpLine(pw, uid, category, NETWORK_DATA, mobileBytesRx, mobileBytesTx,
+                        wifiBytesRx, wifiBytesTx,
+                        mobilePacketsRx, mobilePacketsTx,
+                        wifiPacketsRx, wifiPacketsTx,
+                        mobileActiveTime, mobileActiveCount,
+                        btBytesRx, btBytesTx, mobileWakeup, wifiWakeup,
+                        mobileBytesBgRx, mobileBytesBgTx, wifiBytesBgRx, wifiBytesBgTx,
+                        mobilePacketsBgRx, mobilePacketsBgTx, wifiPacketsBgRx, wifiPacketsBgTx
+                        );
+            }
+
+            // Dump modem controller data, per UID.
+            dumpControllerActivityLine(pw, uid, category, MODEM_CONTROLLER_DATA,
+                    u.getModemControllerActivity(), which);
+
+            // Dump Wifi controller data, per UID.
+            final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
+            final long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
+            final int wifiScanCount = u.getWifiScanCount(which);
+            final int wifiScanCountBg = u.getWifiScanBackgroundCount(which);
+            // Note that 'ActualTime' are unpooled and always since reset (regardless of 'which')
+            final long wifiScanActualTimeMs = (u.getWifiScanActualTime(rawRealtime) + 500) / 1000;
+            final long wifiScanActualTimeMsBg = (u.getWifiScanBackgroundTime(rawRealtime) + 500)
+                    / 1000;
+            final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+            if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0
+                    || wifiScanCountBg != 0 || wifiScanActualTimeMs != 0
+                    || wifiScanActualTimeMsBg != 0 || uidWifiRunningTime != 0) {
+                dumpLine(pw, uid, category, WIFI_DATA, fullWifiLockOnTime, wifiScanTime,
+                        uidWifiRunningTime, wifiScanCount,
+                        /* legacy fields follow, keep at 0 */ 0, 0, 0,
+                        wifiScanCountBg, wifiScanActualTimeMs, wifiScanActualTimeMsBg);
+            }
+
+            dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA,
+                    u.getWifiControllerActivity(), which);
+
+            final Timer bleTimer = u.getBluetoothScanTimer();
+            if (bleTimer != null) {
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (bleTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+                        / 1000;
+                if (totalTime != 0) {
+                    final int count = bleTimer.getCountLocked(which);
+                    final Timer bleTimerBg = u.getBluetoothScanBackgroundTimer();
+                    final int countBg = bleTimerBg != null ? bleTimerBg.getCountLocked(which) : 0;
+                    // 'actualTime' are unpooled and always since reset (regardless of 'which')
+                    final long actualTime = bleTimer.getTotalDurationMsLocked(rawRealtimeMs);
+                    final long actualTimeBg = bleTimerBg != null ?
+                            bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    // Result counters
+                    final int resultCount = u.getBluetoothScanResultCounter() != null ?
+                            u.getBluetoothScanResultCounter().getCountLocked(which) : 0;
+                    final int resultCountBg = u.getBluetoothScanResultBgCounter() != null ?
+                            u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0;
+                    // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimer = u.getBluetoothUnoptimizedScanTimer();
+                    final long unoptimizedScanTotalTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getMaxDurationMsLocked(rawRealtimeMs) : 0;
+                    // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimerBg =
+                            u.getBluetoothUnoptimizedScanBackgroundTimer();
+                    final long unoptimizedScanTotalTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getMaxDurationMsLocked(rawRealtimeMs) : 0;
+
+                    dumpLine(pw, uid, category, BLUETOOTH_MISC_DATA, totalTime, count,
+                            countBg, actualTime, actualTimeBg, resultCount, resultCountBg,
+                            unoptimizedScanTotalTime, unoptimizedScanTotalTimeBg,
+                            unoptimizedScanMaxTime, unoptimizedScanMaxTimeBg);
+                }
+            }
+
+            dumpControllerActivityLine(pw, uid, category, BLUETOOTH_CONTROLLER_DATA,
+                    u.getBluetoothControllerActivity(), which);
+
+            if (u.hasUserActivity()) {
+                args = new Object[Uid.NUM_USER_ACTIVITY_TYPES];
+                boolean hasData = false;
+                for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
+                    int val = u.getUserActivityCount(i, which);
+                    args[i] = val;
+                    if (val != 0) hasData = true;
+                }
+                if (hasData) {
+                    dumpLine(pw, uid /* uid */, category, USER_ACTIVITY_DATA, args);
+                }
+            }
+
+            if (u.getAggregatedPartialWakelockTimer() != null) {
+                final Timer timer = u.getAggregatedPartialWakelockTimer();
+                // Times are since reset (regardless of 'which')
+                final long totTimeMs = timer.getTotalDurationMsLocked(rawRealtimeMs);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTimeMs = bgTimer != null ?
+                        bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                dumpLine(pw, uid, category, AGGREGATED_WAKELOCK_DATA, totTimeMs, bgTimeMs);
+            }
+
+            final ArrayMap<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+                String linePrefix = "";
+                sb.setLength(0);
+                linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL),
+                        rawRealtime, "f", which, linePrefix);
+                final Timer pTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                linePrefix = printWakeLockCheckin(sb, pTimer,
+                        rawRealtime, "p", which, linePrefix);
+                linePrefix = printWakeLockCheckin(sb, pTimer != null ? pTimer.getSubTimer() : null,
+                        rawRealtime, "bp", which, linePrefix);
+                linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW),
+                        rawRealtime, "w", which, linePrefix);
+
+                // Only log if we had at least one wakelock...
+                if (sb.length() > 0) {
+                    String name = wakelocks.keyAt(iw);
+                    if (name.indexOf(',') >= 0) {
+                        name = name.replace(',', '_');
+                    }
+                    if (name.indexOf('\n') >= 0) {
+                        name = name.replace('\n', '_');
+                    }
+                    if (name.indexOf('\r') >= 0) {
+                        name = name.replace('\r', '_');
+                    }
+                    dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
+                }
+            }
+
+            // WiFi Multicast Wakelock Statistics
+            final Timer mcTimer = u.getMulticastWakelockStats();
+            if (mcTimer != null) {
+                final long totalMcWakelockTimeMs =
+                        mcTimer.getTotalTimeLocked(rawRealtime, which) / 1000 ;
+                final int countMcWakelock = mcTimer.getCountLocked(which);
+                if(totalMcWakelockTimeMs > 0) {
+                    dumpLine(pw, uid, category, WIFI_MULTICAST_DATA,
+                            totalMcWakelockTimeMs, countMcWakelock);
+                }
+            }
+
+            final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+            for (int isy=syncs.size()-1; isy>=0; isy--) {
+                final Timer timer = syncs.valueAt(isy);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTime = bgTimer != null ?
+                        bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1;
+                final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
+                if (totalTime != 0) {
+                    dumpLine(pw, uid, category, SYNC_DATA, "\"" + syncs.keyAt(isy) + "\"",
+                            totalTime, count, bgTime, bgCount);
+                }
+            }
+
+            final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+            for (int ij=jobs.size()-1; ij>=0; ij--) {
+                final Timer timer = jobs.valueAt(ij);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTime = bgTimer != null ?
+                        bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1;
+                final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
+                if (totalTime != 0) {
+                    dumpLine(pw, uid, category, JOB_DATA, "\"" + jobs.keyAt(ij) + "\"",
+                            totalTime, count, bgTime, bgCount);
+                }
+            }
+
+            final int[] jobStopReasonCodes = JobParameters.getJobStopReasonCodes();
+            final Object[] jobCompletionArgs = new Object[jobStopReasonCodes.length + 1];
+
+            final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
+            for (int ic=completions.size()-1; ic>=0; ic--) {
+                SparseIntArray types = completions.valueAt(ic);
+                if (types != null) {
+                    jobCompletionArgs[0] = "\"" + completions.keyAt(ic) + "\"";
+                    for (int i = 0; i < jobStopReasonCodes.length; i++) {
+                        jobCompletionArgs[i + 1] = types.get(jobStopReasonCodes[i], 0);
+                    }
+
+                    dumpLine(pw, uid, category, JOB_COMPLETION_DATA, jobCompletionArgs);
+                }
+            }
+
+            // Dump deferred jobs stats
+            u.getDeferredJobsCheckinLineLocked(sb, which);
+            if (sb.length() > 0) {
+                dumpLine(pw, uid, category, JOBS_DEFERRED_DATA, sb.toString());
+            }
+
+            dumpTimer(pw, uid, category, FLASHLIGHT_DATA, u.getFlashlightTurnedOnTimer(),
+                    rawRealtime, which);
+            dumpTimer(pw, uid, category, CAMERA_DATA, u.getCameraTurnedOnTimer(),
+                    rawRealtime, which);
+            dumpTimer(pw, uid, category, VIDEO_DATA, u.getVideoTurnedOnTimer(),
+                    rawRealtime, which);
+            dumpTimer(pw, uid, category, AUDIO_DATA, u.getAudioTurnedOnTimer(),
+                    rawRealtime, which);
+
+            final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            final int NSE = sensors.size();
+            for (int ise=0; ise<NSE; ise++) {
+                final Uid.Sensor se = sensors.valueAt(ise);
+                final int sensorNumber = sensors.keyAt(ise);
+                final Timer timer = se.getSensorTime();
+                if (timer != null) {
+                    // Convert from microseconds to milliseconds with rounding
+                    final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
+                            / 1000;
+                    if (totalTime != 0) {
+                        final int count = timer.getCountLocked(which);
+                        final Timer bgTimer = se.getSensorBackgroundTime();
+                        final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : 0;
+                        // 'actualTime' are unpooled and always since reset (regardless of 'which')
+                        final long actualTime = timer.getTotalDurationMsLocked(rawRealtimeMs);
+                        final long bgActualTime = bgTimer != null ?
+                                bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                        dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime,
+                                count, bgCount, actualTime, bgActualTime);
+                    }
+                }
+            }
+
+            dumpTimer(pw, uid, category, VIBRATOR_DATA, u.getVibratorOnTimer(),
+                    rawRealtime, which);
+
+            dumpTimer(pw, uid, category, FOREGROUND_ACTIVITY_DATA, u.getForegroundActivityTimer(),
+                    rawRealtime, which);
+
+            dumpTimer(pw, uid, category, FOREGROUND_SERVICE_DATA, u.getForegroundServiceTimer(),
+                    rawRealtime, which);
+
+            final Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
+            long totalStateTime = 0;
+            for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
+                final long time = u.getProcessStateTime(ips, rawRealtime, which);
+                totalStateTime += time;
+                stateTimes[ips] = (time + 500) / 1000;
+            }
+            if (totalStateTime > 0) {
+                dumpLine(pw, uid, category, STATE_TIME_DATA, stateTimes);
+            }
+
+            final long userCpuTimeUs = u.getUserCpuTimeUs(which);
+            final long systemCpuTimeUs = u.getSystemCpuTimeUs(which);
+            if (userCpuTimeUs > 0 || systemCpuTimeUs > 0) {
+                dumpLine(pw, uid, category, CPU_DATA, userCpuTimeUs / 1000, systemCpuTimeUs / 1000,
+                        0 /* old cpu power, keep for compatibility */);
+            }
+
+            // If the cpuFreqs is null, then don't bother checking for cpu freq times.
+            if (cpuFreqs != null) {
+                final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
+                // If total cpuFreqTimes is null, then we don't need to check for
+                // screenOffCpuFreqTimes.
+                if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
+                    sb.setLength(0);
+                    for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
+                        if (i != 0) sb.append(',');
+                        sb.append(cpuFreqTimeMs[i]);
+                    }
+                    final long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
+                    if (screenOffCpuFreqTimeMs != null) {
+                        for (int i = 0; i < screenOffCpuFreqTimeMs.length; ++i) {
+                            sb.append(',').append(screenOffCpuFreqTimeMs[i]);
+                        }
+                    } else {
+                        for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
+                            sb.append(",0");
+                        }
+                    }
+                    dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA, UID_TIMES_TYPE_ALL,
+                            cpuFreqTimeMs.length, sb.toString());
+                }
+
+                final long[] timesInFreqMs = new long[getCpuFreqCount()];
+                for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
+                    if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
+                        sb.setLength(0);
+                        for (int i = 0; i < timesInFreqMs.length; ++i) {
+                            if (i != 0) sb.append(',');
+                            sb.append(timesInFreqMs[i]);
+                        }
+                        if (u.getScreenOffCpuFreqTimes(timesInFreqMs, procState)) {
+                            for (int i = 0; i < timesInFreqMs.length; ++i) {
+                                sb.append(',').append(timesInFreqMs[i]);
+                            }
+                        } else {
+                            for (int i = 0; i < timesInFreqMs.length; ++i) {
+                                sb.append(",0");
+                            }
+                        }
+                        dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA,
+                                Uid.UID_PROCESS_TYPES[procState], timesInFreqMs.length,
+                                sb.toString());
+                    }
+                }
+            }
+
+            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
+                    = u.getProcessStats();
+            for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
+                final Uid.Proc ps = processStats.valueAt(ipr);
+
+                final long userMillis = ps.getUserTime(which);
+                final long systemMillis = ps.getSystemTime(which);
+                final long foregroundMillis = ps.getForegroundTime(which);
+                final int starts = ps.getStarts(which);
+                final int numCrashes = ps.getNumCrashes(which);
+                final int numAnrs = ps.getNumAnrs(which);
+
+                if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
+                        || starts != 0 || numAnrs != 0 || numCrashes != 0) {
+                    dumpLine(pw, uid, category, PROCESS_DATA, "\"" + processStats.keyAt(ipr) + "\"",
+                            userMillis, systemMillis, foregroundMillis, starts, numAnrs, numCrashes);
+                }
+            }
+
+            final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats
+                    = u.getPackageStats();
+            for (int ipkg=packageStats.size()-1; ipkg>=0; ipkg--) {
+                final Uid.Pkg ps = packageStats.valueAt(ipkg);
+                int wakeups = 0;
+                final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+                for (int iwa=alarms.size()-1; iwa>=0; iwa--) {
+                    int count = alarms.valueAt(iwa).getCountLocked(which);
+                    wakeups += count;
+                    String name = alarms.keyAt(iwa).replace(',', '_');
+                    dumpLine(pw, uid, category, WAKEUP_ALARM_DATA, name, count);
+                }
+                final ArrayMap<String, ? extends  Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+                for (int isvc=serviceStats.size()-1; isvc>=0; isvc--) {
+                    final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+                    final long startTime = ss.getStartTime(batteryUptime, which);
+                    final int starts = ss.getStarts(which);
+                    final int launches = ss.getLaunches(which);
+                    if (startTime != 0 || starts != 0 || launches != 0) {
+                        dumpLine(pw, uid, category, APK_DATA,
+                                wakeups, // wakeup alarms
+                                packageStats.keyAt(ipkg), // Apk
+                                serviceStats.keyAt(isvc), // service
+                                startTime / 1000, // time spent started, in ms
+                                starts,
+                                launches);
+                    }
+                }
+            }
+        }
+    }
+
+    static final class TimerEntry {
+        final String mName;
+        final int mId;
+        final BatteryStats.Timer mTimer;
+        final long mTime;
+        TimerEntry(String name, int id, BatteryStats.Timer timer, long time) {
+            mName = name;
+            mId = id;
+            mTimer = timer;
+            mTime = time;
+        }
+    }
+
+    private void printmAh(PrintWriter printer, double power) {
+        printer.print(formatCharge(power));
+    }
+
+    private void printmAh(StringBuilder sb, double power) {
+        sb.append(formatCharge(power));
+    }
+
+    @SuppressWarnings("unused")
+    public final void dumpLocked(Context context, PrintWriter pw, String prefix, final int which,
+            int reqUid, boolean wifiOnly) {
+
+        if (which != BatteryStats.STATS_SINCE_CHARGED) {
+            pw.println("ERROR: BatteryStats.dump called for which type " + which
+                    + " but only STATS_SINCE_CHARGED is supported");
+            return;
+        }
+
+        final long rawUptime = SystemClock.uptimeMillis() * 1000;
+        final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
+        final long rawRealtimeMs = (rawRealtime + 500) / 1000;
+        final long batteryUptime = getBatteryUptime(rawUptime);
+
+        final long whichBatteryUptime = computeBatteryUptime(rawUptime, which);
+        final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which);
+        final long totalRealtime = computeRealtime(rawRealtime, which);
+        final long totalUptime = computeUptime(rawUptime, which);
+        final long whichBatteryScreenOffUptime = computeBatteryScreenOffUptime(rawUptime, which);
+        final long whichBatteryScreenOffRealtime = computeBatteryScreenOffRealtime(rawRealtime,
+                which);
+        final long batteryTimeRemaining = computeBatteryTimeRemaining(rawRealtime);
+        final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
+        final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
+
+        final StringBuilder sb = new StringBuilder(128);
+
+        final SparseArray<? extends Uid> uidStats = getUidStats();
+        final int NU = uidStats.size();
+
+        final int estimatedBatteryCapacity = getEstimatedBatteryCapacity();
+        if (estimatedBatteryCapacity > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                sb.append("  Estimated battery capacity: ");
+            sb.append(formatCharge(estimatedBatteryCapacity));
+                sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        final int lastLearnedBatteryCapacity = getLearnedBatteryCapacity();
+        if (lastLearnedBatteryCapacity > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Last learned battery capacity: ");
+            sb.append(formatCharge(lastLearnedBatteryCapacity / 1000));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+        final int minLearnedBatteryCapacity = getMinLearnedBatteryCapacity();
+        if (minLearnedBatteryCapacity > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Min learned battery capacity: ");
+            sb.append(formatCharge(minLearnedBatteryCapacity / 1000));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+        final int maxLearnedBatteryCapacity = getMaxLearnedBatteryCapacity();
+        if (maxLearnedBatteryCapacity > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Max learned battery capacity: ");
+            sb.append(formatCharge(maxLearnedBatteryCapacity / 1000));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Time on battery: ");
+        formatTimeMs(sb, whichBatteryRealtime / 1000); sb.append("(");
+        sb.append(formatRatioLocked(whichBatteryRealtime, totalRealtime));
+        sb.append(") realtime, ");
+        formatTimeMs(sb, whichBatteryUptime / 1000);
+        sb.append("("); sb.append(formatRatioLocked(whichBatteryUptime, whichBatteryRealtime));
+        sb.append(") uptime");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Time on battery screen off: ");
+        formatTimeMs(sb, whichBatteryScreenOffRealtime / 1000); sb.append("(");
+        sb.append(formatRatioLocked(whichBatteryScreenOffRealtime, whichBatteryRealtime));
+        sb.append(") realtime, ");
+        formatTimeMs(sb, whichBatteryScreenOffUptime / 1000);
+        sb.append("(");
+        sb.append(formatRatioLocked(whichBatteryScreenOffUptime, whichBatteryRealtime));
+        sb.append(") uptime");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Time on battery screen doze: ");
+        formatTimeMs(sb, screenDozeTime / 1000); sb.append("(");
+        sb.append(formatRatioLocked(screenDozeTime, whichBatteryRealtime));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+                sb.append("  Total run time: ");
+                formatTimeMs(sb, totalRealtime / 1000);
+                sb.append("realtime, ");
+                formatTimeMs(sb, totalUptime / 1000);
+                sb.append("uptime");
+        pw.println(sb.toString());
+        if (batteryTimeRemaining >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Battery time remaining: ");
+                    formatTimeMs(sb, batteryTimeRemaining / 1000);
+            pw.println(sb.toString());
+        }
+        if (chargeTimeRemaining >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Charge time remaining: ");
+                    formatTimeMs(sb, chargeTimeRemaining / 1000);
+            pw.println(sb.toString());
+        }
+
+        final long dischargeCount = getUahDischarge(which);
+        if (dischargeCount >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                sb.append("  Discharge: ");
+            sb.append(formatCharge(dischargeCount / 1000.0));
+                sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
+        if (dischargeScreenOffCount >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                sb.append("  Screen off discharge: ");
+            sb.append(formatCharge(dischargeScreenOffCount / 1000.0));
+                sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
+        if (dischargeScreenDozeCount >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Screen doze discharge: ");
+            sb.append(formatCharge(dischargeScreenDozeCount / 1000.0));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
+        if (dischargeScreenOnCount >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                sb.append("  Screen on discharge: ");
+            sb.append(formatCharge(dischargeScreenOnCount / 1000.0));
+                sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        final long dischargeLightDozeCount = getUahDischargeLightDoze(which);
+        if (dischargeLightDozeCount >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Device light doze discharge: ");
+            sb.append(formatCharge(dischargeLightDozeCount / 1000.0));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        final long dischargeDeepDozeCount = getUahDischargeDeepDoze(which);
+        if (dischargeDeepDozeCount >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Device deep doze discharge: ");
+            sb.append(formatCharge(dischargeDeepDozeCount / 1000.0));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        pw.print("  Start clock time: ");
+        pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss", getStartClockTime()).toString());
+
+        final long screenOnTime = getScreenOnTime(rawRealtime, which);
+        final long interactiveTime = getInteractiveTime(rawRealtime, which);
+        final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
+        final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT,
+                rawRealtime, which);
+        final long deviceIdleModeFullTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP,
+                rawRealtime, which);
+        final long deviceLightIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT,
+                rawRealtime, which);
+        final long deviceIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_DEEP,
+                rawRealtime, which);
+        final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
+        final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
+        final long wifiOnTime = getWifiOnTime(rawRealtime, which);
+        sb.setLength(0);
+        sb.append(prefix);
+                sb.append("  Screen on: "); formatTimeMs(sb, screenOnTime / 1000);
+                sb.append("("); sb.append(formatRatioLocked(screenOnTime, whichBatteryRealtime));
+                sb.append(") "); sb.append(getScreenOnCount(which));
+                sb.append("x, Interactive: "); formatTimeMs(sb, interactiveTime / 1000);
+                sb.append("("); sb.append(formatRatioLocked(interactiveTime, whichBatteryRealtime));
+                sb.append(")");
+        pw.println(sb.toString());
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Screen brightnesses:");
+        boolean didOne = false;
+        for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+            final long time = getScreenBrightnessTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n    ");
+            sb.append(prefix);
+            didOne = true;
+            sb.append(SCREEN_BRIGHTNESS_NAMES[i]);
+            sb.append(" ");
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, screenOnTime));
+            sb.append(")");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+        if (powerSaveModeEnabledTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Power save mode enabled: ");
+                    formatTimeMs(sb, powerSaveModeEnabledTime / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(powerSaveModeEnabledTime, whichBatteryRealtime));
+                    sb.append(")");
+            pw.println(sb.toString());
+        }
+        if (deviceLightIdlingTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Device light idling: ");
+                    formatTimeMs(sb, deviceLightIdlingTime / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(deviceLightIdlingTime, whichBatteryRealtime));
+                    sb.append(") "); sb.append(getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
+                    sb.append("x");
+            pw.println(sb.toString());
+        }
+        if (deviceIdleModeLightTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Idle mode light time: ");
+                    formatTimeMs(sb, deviceIdleModeLightTime / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(deviceIdleModeLightTime, whichBatteryRealtime));
+                    sb.append(") ");
+                    sb.append(getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which));
+                    sb.append("x");
+                    sb.append(" -- longest ");
+                    formatTimeMs(sb, getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
+            pw.println(sb.toString());
+        }
+        if (deviceIdlingTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Device full idling: ");
+                    formatTimeMs(sb, deviceIdlingTime / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime));
+                    sb.append(") "); sb.append(getDeviceIdlingCount(DEVICE_IDLE_MODE_DEEP, which));
+                    sb.append("x");
+            pw.println(sb.toString());
+        }
+        if (deviceIdleModeFullTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Idle mode full time: ");
+                    formatTimeMs(sb, deviceIdleModeFullTime / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(deviceIdleModeFullTime, whichBatteryRealtime));
+                    sb.append(") ");
+                    sb.append(getDeviceIdleModeCount(DEVICE_IDLE_MODE_DEEP, which));
+                    sb.append("x");
+                    sb.append(" -- longest ");
+                    formatTimeMs(sb, getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP));
+            pw.println(sb.toString());
+        }
+        if (phoneOnTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000);
+                    sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime));
+                    sb.append(") "); sb.append(getPhoneOnCount(which)); sb.append("x");
+        }
+        final int connChanges = getNumConnectivityChange(which);
+        if (connChanges != 0) {
+            pw.print(prefix);
+            pw.print("  Connectivity changes: "); pw.println(connChanges);
+        }
+
+        // Calculate wakelock times across all uids.
+        long fullWakeLockTimeTotalMicros = 0;
+        long partialWakeLockTimeTotalMicros = 0;
+
+        final ArrayList<TimerEntry> timers = new ArrayList<>();
+
+        for (int iu = 0; iu < NU; iu++) {
+            final Uid u = uidStats.valueAt(iu);
+
+            final ArrayMap<String, ? extends Uid.Wakelock> wakelocks
+                    = u.getWakelockStats();
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+
+                final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+                if (fullWakeTimer != null) {
+                    fullWakeLockTimeTotalMicros += fullWakeTimer.getTotalTimeLocked(
+                            rawRealtime, which);
+                }
+
+                final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (partialWakeTimer != null) {
+                    final long totalTimeMicros = partialWakeTimer.getTotalTimeLocked(
+                            rawRealtime, which);
+                    if (totalTimeMicros > 0) {
+                        if (reqUid < 0) {
+                            // Only show the ordered list of all wake
+                            // locks if the caller is not asking for data
+                            // about a specific uid.
+                            timers.add(new TimerEntry(wakelocks.keyAt(iw), u.getUid(),
+                                    partialWakeTimer, totalTimeMicros));
+                        }
+                        partialWakeLockTimeTotalMicros += totalTimeMicros;
+                    }
+                }
+            }
+        }
+
+        final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+        final long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+        final long btRxTotalBytes = getNetworkActivityBytes(NETWORK_BT_RX_DATA, which);
+        final long btTxTotalBytes = getNetworkActivityBytes(NETWORK_BT_TX_DATA, which);
+
+        if (fullWakeLockTimeTotalMicros != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Total full wakelock time: "); formatTimeMsNoSpace(sb,
+                            (fullWakeLockTimeTotalMicros + 500) / 1000);
+            pw.println(sb.toString());
+        }
+
+        if (partialWakeLockTimeTotalMicros != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Total partial wakelock time: "); formatTimeMsNoSpace(sb,
+                            (partialWakeLockTimeTotalMicros + 500) / 1000);
+            pw.println(sb.toString());
+        }
+
+        final long multicastWakeLockTimeTotalMicros =
+                getWifiMulticastWakelockTime(rawRealtime, which);
+        final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which);
+        if (multicastWakeLockTimeTotalMicros != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Total WiFi Multicast wakelock Count: ");
+            sb.append(multicastWakeLockCountTotal);
+            pw.println(sb.toString());
+
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Total WiFi Multicast wakelock time: ");
+            formatTimeMsNoSpace(sb, (multicastWakeLockTimeTotalMicros + 500) / 1000);
+            pw.println(sb.toString());
+        }
+
+        final int numDisplays = getDisplayCount();
+        if (numDisplays > 1) {
+            pw.println("");
+            pw.print(prefix);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  MULTI-DISPLAY POWER SUMMARY START");
+            pw.println(sb.toString());
+
+            for (int display = 0; display < numDisplays; display++) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("  Display ");
+                sb.append(display);
+                sb.append(" Statistics:");
+                pw.println(sb.toString());
+
+                final long displayScreenOnTime = getDisplayScreenOnTime(display, rawRealtime);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Screen on: ");
+                formatTimeMs(sb, displayScreenOnTime / 1000);
+                sb.append("(");
+                sb.append(formatRatioLocked(displayScreenOnTime, whichBatteryRealtime));
+                sb.append(") ");
+                pw.println(sb.toString());
+
+                sb.setLength(0);
+                sb.append("    Screen brightness levels:");
+                didOne = false;
+                for (int bin = 0; bin < NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
+                    final long timeUs = getDisplayScreenBrightnessTime(display, bin, rawRealtime);
+                    if (timeUs == 0) {
+                        continue;
+                    }
+                    didOne = true;
+                    sb.append("\n      ");
+                    sb.append(prefix);
+                    sb.append(SCREEN_BRIGHTNESS_NAMES[bin]);
+                    sb.append(" ");
+                    formatTimeMs(sb, timeUs / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(timeUs, displayScreenOnTime));
+                    sb.append(")");
+                }
+                if (!didOne) sb.append(" (no activity)");
+                pw.println(sb.toString());
+
+                final long displayScreenDozeTimeUs = getDisplayScreenDozeTime(display, rawRealtime);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Screen Doze: ");
+                formatTimeMs(sb, displayScreenDozeTimeUs / 1000);
+                sb.append("(");
+                sb.append(formatRatioLocked(displayScreenDozeTimeUs, whichBatteryRealtime));
+                sb.append(") ");
+                pw.println(sb.toString());
+            }
+            pw.print(prefix);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  MULTI-DISPLAY POWER SUMMARY END");
+            pw.println(sb.toString());
+        }
+
+        pw.println("");
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  CONNECTIVITY POWER SUMMARY START");
+        pw.println(sb.toString());
+
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Logging duration for connectivity statistics: ");
+        formatTimeMs(sb, whichBatteryRealtime / 1000);
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Cellular Statistics:");
+        pw.println(sb.toString());
+
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Cellular kernel active time: ");
+        final long mobileActiveTime = getMobileRadioActiveTime(rawRealtime, which);
+        formatTimeMs(sb, mobileActiveTime / 1000);
+        sb.append("("); sb.append(formatRatioLocked(mobileActiveTime, whichBatteryRealtime));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME,
+                getModemControllerActivity(), which);
+
+        printCellularPerRatBreakdown(pw, sb, prefix + "     ", rawRealtimeMs);
+
+        pw.print("     Cellular data received: "); pw.println(formatBytesLocked(mobileRxTotalBytes));
+        pw.print("     Cellular data sent: "); pw.println(formatBytesLocked(mobileTxTotalBytes));
+        pw.print("     Cellular packets received: "); pw.println(mobileRxTotalPackets);
+        pw.print("     Cellular packets sent: "); pw.println(mobileTxTotalPackets);
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Cellular Radio Access Technology:");
+        didOne = false;
+        for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+            final long time = getPhoneDataConnectionTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n       ");
+            sb.append(prefix);
+            didOne = true;
+            sb.append(i < DATA_CONNECTION_NAMES.length ? DATA_CONNECTION_NAMES[i] : "ERROR");
+            sb.append(" ");
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Cellular Rx signal strength (RSRP):");
+        final String[] cellularRxSignalStrengthDescription = new String[]{
+            "very poor (less than -128dBm): ",
+            "poor (-128dBm to -118dBm): ",
+            "moderate (-118dBm to -108dBm): ",
+            "good (-108dBm to -98dBm): ",
+            "great (greater than -98dBm): "};
+        didOne = false;
+        final int numCellularRxBins = Math.min(CellSignalStrength.getNumSignalStrengthLevels(),
+            cellularRxSignalStrengthDescription.length);
+        for (int i=0; i<numCellularRxBins; i++) {
+            final long time = getPhoneSignalStrengthTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n       ");
+            sb.append(prefix);
+            didOne = true;
+            sb.append(cellularRxSignalStrengthDescription[i]);
+            sb.append(" ");
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Wifi Statistics:");
+        pw.println(sb.toString());
+
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Wifi kernel active time: ");
+        final long wifiActiveTime = getWifiActiveTime(rawRealtime, which);
+        formatTimeMs(sb, wifiActiveTime / 1000);
+        sb.append("("); sb.append(formatRatioLocked(wifiActiveTime, whichBatteryRealtime));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        printControllerActivity(pw, sb, prefix, WIFI_CONTROLLER_NAME,
+                getWifiControllerActivity(), which);
+
+        pw.print("     Wifi data received: "); pw.println(formatBytesLocked(wifiRxTotalBytes));
+        pw.print("     Wifi data sent: "); pw.println(formatBytesLocked(wifiTxTotalBytes));
+        pw.print("     Wifi packets received: "); pw.println(wifiRxTotalPackets);
+        pw.print("     Wifi packets sent: "); pw.println(wifiTxTotalPackets);
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Wifi states:");
+        didOne = false;
+        for (int i=0; i<NUM_WIFI_STATES; i++) {
+            final long time = getWifiStateTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n       ");
+            didOne = true;
+            sb.append(WIFI_STATE_NAMES[i]);
+            sb.append(" ");
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Wifi supplicant states:");
+        didOne = false;
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            final long time = getWifiSupplStateTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n       ");
+            didOne = true;
+            sb.append(WIFI_SUPPL_STATE_NAMES[i]);
+            sb.append(" ");
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Wifi Rx signal strength (RSSI):");
+        final String[] wifiRxSignalStrengthDescription = new String[]{
+            "very poor (less than -88.75dBm): ",
+            "poor (-88.75 to -77.5dBm): ",
+            "moderate (-77.5dBm to -66.25dBm): ",
+            "good (-66.25dBm to -55dBm): ",
+            "great (greater than -55dBm): "};
+        didOne = false;
+        final int numWifiRxBins = Math.min(NUM_WIFI_SIGNAL_STRENGTH_BINS,
+            wifiRxSignalStrengthDescription.length);
+        for (int i=0; i<numWifiRxBins; i++) {
+            final long time = getWifiSignalStrengthTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n    ");
+            sb.append(prefix);
+            didOne = true;
+            sb.append("     ");
+            sb.append(wifiRxSignalStrengthDescription[i]);
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  GPS Statistics:");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     GPS signal quality (Top 4 Average CN0):");
+        final String[] gpsSignalQualityDescription = new String[]{
+            "poor (less than 20 dBHz): ",
+            "good (greater than 20 dBHz): "};
+        final int numGpsSignalQualityBins = Math.min(
+                GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS,
+                gpsSignalQualityDescription.length);
+        for (int i=0; i<numGpsSignalQualityBins; i++) {
+            final long time = getGpsSignalQualityTime(i, rawRealtime, which);
+            sb.append("\n    ");
+            sb.append(prefix);
+            sb.append("  ");
+            sb.append(gpsSignalQualityDescription[i]);
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+        }
+        pw.println(sb.toString());
+
+        final long gpsBatteryDrainMaMs = getGpsBatteryDrainMaMs();
+        if (gpsBatteryDrainMaMs > 0) {
+            pw.print(prefix);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     GPS Battery Drain: ");
+            sb.append(new DecimalFormat("#.##").format(
+                    ((double) gpsBatteryDrainMaMs) / (3600 * 1000)));
+            sb.append("mAh");
+            pw.println(sb.toString());
+        }
+
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  CONNECTIVITY POWER SUMMARY END");
+        pw.println(sb.toString());
+        pw.println("");
+
+        pw.print(prefix);
+        pw.print("  Bluetooth total received: "); pw.print(formatBytesLocked(btRxTotalBytes));
+        pw.print(", sent: "); pw.println(formatBytesLocked(btTxTotalBytes));
+
+        final long bluetoothScanTimeMs = getBluetoothScanTime(rawRealtime, which) / 1000;
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Bluetooth scan time: "); formatTimeMs(sb, bluetoothScanTimeMs);
+        pw.println(sb.toString());
+
+        printControllerActivity(pw, sb, prefix, "Bluetooth", getBluetoothControllerActivity(),
+                which);
+
+        pw.println();
+
+        pw.print(prefix); pw.println("  Device battery use since last full charge");
+        pw.print(prefix); pw.print("    Amount discharged (lower bound): ");
+        pw.println(getLowDischargeAmountSinceCharge());
+        pw.print(prefix); pw.print("    Amount discharged (upper bound): ");
+        pw.println(getHighDischargeAmountSinceCharge());
+        pw.print(prefix); pw.print("    Amount discharged while screen on: ");
+        pw.println(getDischargeAmountScreenOnSinceCharge());
+        pw.print(prefix); pw.print("    Amount discharged while screen off: ");
+        pw.println(getDischargeAmountScreenOffSinceCharge());
+        pw.print(prefix); pw.print("    Amount discharged while screen doze: ");
+        pw.println(getDischargeAmountScreenDozeSinceCharge());
+        pw.println();
+
+
+        BatteryUsageStats stats = getBatteryUsageStats(context, true /* detailed */);
+        stats.dump(pw, prefix);
+
+        List<UidMobileRadioStats> uidMobileRadioStats =
+                getUidMobileRadioStats(stats.getUidBatteryConsumers());
+        if (uidMobileRadioStats.size() > 0) {
+            pw.print(prefix);
+            pw.println("  Per-app mobile ms per packet:");
+            long totalTime = 0;
+            for (int i = 0; i < uidMobileRadioStats.size(); i++) {
+                final UidMobileRadioStats mrs = uidMobileRadioStats.get(i);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Uid ");
+                UserHandle.formatUid(sb, mrs.uid);
+                sb.append(": ");
+                sb.append(formatValue(mrs.millisecondsPerPacket));
+                sb.append(" (");
+                sb.append(mrs.rxPackets + mrs.txPackets);
+                sb.append(" packets over ");
+                formatTimeMsNoSpace(sb, mrs.radioActiveMs);
+                sb.append(") ");
+                sb.append(mrs.radioActiveCount);
+                sb.append("x");
+                pw.println(sb);
+                totalTime += mrs.radioActiveMs;
+            }
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("    TOTAL TIME: ");
+            formatTimeMs(sb, totalTime);
+            sb.append("(");
+            sb.append(formatRatioLocked(totalTime, whichBatteryRealtime));
+            sb.append(")");
+            pw.println(sb);
+            pw.println();
+        }
+
+        final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
+            @Override
+            public int compare(TimerEntry lhs, TimerEntry rhs) {
+                long lhsTime = lhs.mTime;
+                long rhsTime = rhs.mTime;
+                if (lhsTime < rhsTime) {
+                    return 1;
+                }
+                if (lhsTime > rhsTime) {
+                    return -1;
+                }
+                return 0;
+            }
+        };
+
+        if (reqUid < 0) {
+            final Map<String, ? extends Timer> kernelWakelocks
+                    = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                final ArrayList<TimerEntry> ktimers = new ArrayList<>();
+                for (Map.Entry<String, ? extends Timer> ent
+                        : kernelWakelocks.entrySet()) {
+                    final Timer timer = ent.getValue();
+                    final long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
+                    if (totalTimeMillis > 0) {
+                        ktimers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
+                    }
+                }
+                if (ktimers.size() > 0) {
+                    Collections.sort(ktimers, timerComparator);
+                    pw.print(prefix); pw.println("  All kernel wake locks:");
+                    for (int i=0; i<ktimers.size(); i++) {
+                        final TimerEntry timer = ktimers.get(i);
+                        String linePrefix = ": ";
+                        sb.setLength(0);
+                        sb.append(prefix);
+                        sb.append("  Kernel Wake lock ");
+                        sb.append(timer.mName);
+                        linePrefix = printWakeLock(sb, timer.mTimer, rawRealtime, null,
+                                which, linePrefix);
+                        if (!linePrefix.equals(": ")) {
+                            sb.append(" realtime");
+                            // Only print out wake locks that were held
+                            pw.println(sb.toString());
+                        }
+                    }
+                    pw.println();
+                }
+            }
+
+            if (timers.size() > 0) {
+                Collections.sort(timers, timerComparator);
+                pw.print(prefix); pw.println("  All partial wake locks:");
+                for (int i=0; i<timers.size(); i++) {
+                    TimerEntry timer = timers.get(i);
+                    sb.setLength(0);
+                    sb.append("  Wake lock ");
+                    UserHandle.formatUid(sb, timer.mId);
+                    sb.append(" ");
+                    sb.append(timer.mName);
+                    printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+                    sb.append(" realtime");
+                    pw.println(sb.toString());
+                }
+                timers.clear();
+                pw.println();
+            }
+
+            final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                pw.print(prefix); pw.println("  All wakeup reasons:");
+                final ArrayList<TimerEntry> reasons = new ArrayList<>();
+                for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+                    final Timer timer = ent.getValue();
+                    reasons.add(new TimerEntry(ent.getKey(), 0, timer,
+                            timer.getCountLocked(which)));
+                }
+                Collections.sort(reasons, timerComparator);
+                for (int i=0; i<reasons.size(); i++) {
+                    TimerEntry timer = reasons.get(i);
+                    String linePrefix = ": ";
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("  Wakeup reason ");
+                    sb.append(timer.mName);
+                    printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+                    sb.append(" realtime");
+                    pw.println(sb.toString());
+                }
+                pw.println();
+            }
+        }
+
+        final LongSparseArray<? extends Timer> mMemoryStats = getKernelMemoryStats();
+        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:");
+            for (int i = 0; i < cpuFreqs.length; ++i) {
+                sb.append(' ').append(cpuFreqs[i]);
+            }
+            pw.println(sb.toString());
+            pw.println();
+        }
+
+        for (int iu=0; iu<NU; iu++) {
+            final int uid = uidStats.keyAt(iu);
+            if (reqUid >= 0 && uid != reqUid && uid != Process.SYSTEM_UID) {
+                continue;
+            }
+
+            final Uid u = uidStats.valueAt(iu);
+
+            pw.print(prefix);
+            pw.print("  ");
+            UserHandle.formatUid(pw, uid);
+            pw.println(":");
+            boolean uidActivity = false;
+
+            final long mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+            final long mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+            final long wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+            final long wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+            final long btRxBytes = u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which);
+            final long btTxBytes = u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which);
+
+            final long mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+            final long mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+            final long wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+            final long wifiTxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+
+            final long uidMobileActiveTime = u.getMobileRadioActiveTime(which);
+            final int uidMobileActiveCount = u.getMobileRadioActiveCount(which);
+
+            final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
+            final long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
+            final int wifiScanCount = u.getWifiScanCount(which);
+            final int wifiScanCountBg = u.getWifiScanBackgroundCount(which);
+            // 'actualTime' are unpooled and always since reset (regardless of 'which')
+            final long wifiScanActualTime = u.getWifiScanActualTime(rawRealtime);
+            final long wifiScanActualTimeBg = u.getWifiScanBackgroundTime(rawRealtime);
+            final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+
+            final long mobileWakeup = u.getMobileRadioApWakeupCount(which);
+            final long wifiWakeup = u.getWifiRadioApWakeupCount(which);
+
+            if (mobileRxBytes > 0 || mobileTxBytes > 0
+                    || mobileRxPackets > 0 || mobileTxPackets > 0) {
+                pw.print(prefix); pw.print("    Mobile network: ");
+                        pw.print(formatBytesLocked(mobileRxBytes)); pw.print(" received, ");
+                        pw.print(formatBytesLocked(mobileTxBytes));
+                        pw.print(" sent (packets "); pw.print(mobileRxPackets);
+                        pw.print(" received, "); pw.print(mobileTxPackets); pw.println(" sent)");
+            }
+            if (uidMobileActiveTime > 0 || uidMobileActiveCount > 0) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("    Mobile radio active: ");
+                formatTimeMs(sb, uidMobileActiveTime / 1000);
+                sb.append("(");
+                sb.append(formatRatioLocked(uidMobileActiveTime, mobileActiveTime));
+                sb.append(") "); sb.append(uidMobileActiveCount); sb.append("x");
+                long packets = mobileRxPackets + mobileTxPackets;
+                if (packets == 0) {
+                    packets = 1;
+                }
+                sb.append(" @ ");
+                sb.append(formatCharge(uidMobileActiveTime / 1000 / (double) packets));
+                sb.append(" mspp");
+                pw.println(sb.toString());
+            }
+
+            if (mobileWakeup > 0) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Mobile radio AP wakeups: ");
+                sb.append(mobileWakeup);
+                pw.println(sb.toString());
+            }
+
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ",
+                CELLULAR_CONTROLLER_NAME, u.getModemControllerActivity(), which);
+
+            if (wifiRxBytes > 0 || wifiTxBytes > 0 || wifiRxPackets > 0 || wifiTxPackets > 0) {
+                pw.print(prefix); pw.print("    Wi-Fi network: ");
+                        pw.print(formatBytesLocked(wifiRxBytes)); pw.print(" received, ");
+                        pw.print(formatBytesLocked(wifiTxBytes));
+                        pw.print(" sent (packets "); pw.print(wifiRxPackets);
+                        pw.print(" received, "); pw.print(wifiTxPackets); pw.println(" sent)");
+            }
+
+            if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0
+                    || wifiScanCountBg != 0 || wifiScanActualTime != 0 || wifiScanActualTimeBg != 0
+                    || uidWifiRunningTime != 0) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("    Wifi Running: ");
+                        formatTimeMs(sb, uidWifiRunningTime / 1000);
+                        sb.append("("); sb.append(formatRatioLocked(uidWifiRunningTime,
+                                whichBatteryRealtime)); sb.append(")\n");
+                sb.append(prefix); sb.append("    Full Wifi Lock: ");
+                        formatTimeMs(sb, fullWifiLockOnTime / 1000);
+                        sb.append("("); sb.append(formatRatioLocked(fullWifiLockOnTime,
+                                whichBatteryRealtime)); sb.append(")\n");
+                sb.append(prefix); sb.append("    Wifi Scan (blamed): ");
+                        formatTimeMs(sb, wifiScanTime / 1000);
+                        sb.append("("); sb.append(formatRatioLocked(wifiScanTime,
+                                whichBatteryRealtime)); sb.append(") ");
+                                sb.append(wifiScanCount);
+                                sb.append("x\n");
+                // actual and background times are unpooled and since reset (regardless of 'which')
+                sb.append(prefix); sb.append("    Wifi Scan (actual): ");
+                        formatTimeMs(sb, wifiScanActualTime / 1000);
+                        sb.append("("); sb.append(formatRatioLocked(wifiScanActualTime,
+                                computeBatteryRealtime(rawRealtime, STATS_SINCE_CHARGED)));
+                                sb.append(") ");
+                                sb.append(wifiScanCount);
+                                sb.append("x\n");
+                sb.append(prefix); sb.append("    Background Wifi Scan: ");
+                        formatTimeMs(sb, wifiScanActualTimeBg / 1000);
+                        sb.append("("); sb.append(formatRatioLocked(wifiScanActualTimeBg,
+                                computeBatteryRealtime(rawRealtime, STATS_SINCE_CHARGED)));
+                                sb.append(") ");
+                                sb.append(wifiScanCountBg);
+                                sb.append("x");
+                pw.println(sb.toString());
+            }
+
+            if (wifiWakeup > 0) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    WiFi AP wakeups: ");
+                sb.append(wifiWakeup);
+                pw.println(sb.toString());
+            }
+
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ", WIFI_CONTROLLER_NAME,
+                    u.getWifiControllerActivity(), which);
+
+            if (btRxBytes > 0 || btTxBytes > 0) {
+                pw.print(prefix); pw.print("    Bluetooth network: ");
+                pw.print(formatBytesLocked(btRxBytes)); pw.print(" received, ");
+                pw.print(formatBytesLocked(btTxBytes));
+                pw.println(" sent");
+            }
+
+            final Timer bleTimer = u.getBluetoothScanTimer();
+            if (bleTimer != null) {
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTimeMs = (bleTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+                        / 1000;
+                if (totalTimeMs != 0) {
+                    final int count = bleTimer.getCountLocked(which);
+                    final Timer bleTimerBg = u.getBluetoothScanBackgroundTimer();
+                    final int countBg = bleTimerBg != null ? bleTimerBg.getCountLocked(which) : 0;
+                    // 'actualTime' are unpooled and always since reset (regardless of 'which')
+                    final long actualTimeMs = bleTimer.getTotalDurationMsLocked(rawRealtimeMs);
+                    final long actualTimeMsBg = bleTimerBg != null ?
+                            bleTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    // Result counters
+                    final int resultCount = u.getBluetoothScanResultCounter() != null ?
+                            u.getBluetoothScanResultCounter().getCountLocked(which) : 0;
+                    final int resultCountBg = u.getBluetoothScanResultBgCounter() != null ?
+                            u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0;
+                    // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimer = u.getBluetoothUnoptimizedScanTimer();
+                    final long unoptimizedScanTotalTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTime = unoptimizedScanTimer != null ?
+                            unoptimizedScanTimer.getMaxDurationMsLocked(rawRealtimeMs) : 0;
+                    // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+                    final Timer unoptimizedScanTimerBg =
+                            u.getBluetoothUnoptimizedScanBackgroundTimer();
+                    final long unoptimizedScanTotalTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                    final long unoptimizedScanMaxTimeBg = unoptimizedScanTimerBg != null ?
+                            unoptimizedScanTimerBg.getMaxDurationMsLocked(rawRealtimeMs) : 0;
+
+                    sb.setLength(0);
+                    if (actualTimeMs != totalTimeMs) {
+                        sb.append(prefix);
+                        sb.append("    Bluetooth Scan (total blamed realtime): ");
+                        formatTimeMs(sb, totalTimeMs);
+                        sb.append(" (");
+                        sb.append(count);
+                        sb.append(" times)");
+                        if (bleTimer.isRunningLocked()) {
+                            sb.append(" (currently running)");
+                        }
+                        sb.append("\n");
+                    }
+
+                    sb.append(prefix);
+                    sb.append("    Bluetooth Scan (total actual realtime): ");
+                    formatTimeMs(sb, actualTimeMs); // since reset, ignores 'which'
+                    sb.append(" (");
+                    sb.append(count);
+                    sb.append(" times)");
+                    if (bleTimer.isRunningLocked()) {
+                            sb.append(" (currently running)");
+                    }
+                    sb.append("\n");
+                    if (actualTimeMsBg > 0 || countBg > 0) {
+                        sb.append(prefix);
+                        sb.append("    Bluetooth Scan (background realtime): ");
+                        formatTimeMs(sb, actualTimeMsBg); // since reset, ignores 'which'
+                        sb.append(" (");
+                        sb.append(countBg);
+                        sb.append(" times)");
+                        if (bleTimerBg != null && bleTimerBg.isRunningLocked()) {
+                            sb.append(" (currently running in background)");
+                        }
+                        sb.append("\n");
+                    }
+
+                    sb.append(prefix);
+                    sb.append("    Bluetooth Scan Results: ");
+                    sb.append(resultCount);
+                    sb.append(" (");
+                    sb.append(resultCountBg);
+                    sb.append(" in background)");
+
+                    if (unoptimizedScanTotalTime > 0 || unoptimizedScanTotalTimeBg > 0) {
+                        sb.append("\n");
+                        sb.append(prefix);
+                        sb.append("    Unoptimized Bluetooth Scan (realtime): ");
+                        formatTimeMs(sb, unoptimizedScanTotalTime); // since reset, ignores 'which'
+                        sb.append(" (max ");
+                        formatTimeMs(sb, unoptimizedScanMaxTime); // since reset, ignores 'which'
+                        sb.append(")");
+                        if (unoptimizedScanTimer != null
+                                && unoptimizedScanTimer.isRunningLocked()) {
+                            sb.append(" (currently running unoptimized)");
+                        }
+                        if (unoptimizedScanTimerBg != null && unoptimizedScanTotalTimeBg > 0) {
+                            sb.append("\n");
+                            sb.append(prefix);
+                            sb.append("    Unoptimized Bluetooth Scan (background realtime): ");
+                            formatTimeMs(sb, unoptimizedScanTotalTimeBg); // since reset
+                            sb.append(" (max ");
+                            formatTimeMs(sb, unoptimizedScanMaxTimeBg); // since reset
+                            sb.append(")");
+                            if (unoptimizedScanTimerBg.isRunningLocked()) {
+                                sb.append(" (currently running unoptimized in background)");
+                            }
+                        }
+                    }
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
+
+
+            if (u.hasUserActivity()) {
+                boolean hasData = false;
+                for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
+                    final int val = u.getUserActivityCount(i, which);
+                    if (val != 0) {
+                        if (!hasData) {
+                            sb.setLength(0);
+                            sb.append("    User activity: ");
+                            hasData = true;
+                        } else {
+                            sb.append(", ");
+                        }
+                        sb.append(val);
+                        sb.append(" ");
+                        sb.append(Uid.USER_ACTIVITY_TYPES[i]);
+                    }
+                }
+                if (hasData) {
+                    pw.println(sb.toString());
+                }
+            }
+
+            final ArrayMap<String, ? extends Uid.Wakelock> wakelocks
+                    = u.getWakelockStats();
+            long totalFullWakelock = 0, totalPartialWakelock = 0, totalWindowWakelock = 0;
+            long totalDrawWakelock = 0;
+            int countWakelock = 0;
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+                String linePrefix = ": ";
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Wake lock ");
+                sb.append(wakelocks.keyAt(iw));
+                linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), rawRealtime,
+                        "full", which, linePrefix);
+                final Timer pTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                linePrefix = printWakeLock(sb, pTimer, rawRealtime,
+                        "partial", which, linePrefix);
+                linePrefix = printWakeLock(sb, pTimer != null ? pTimer.getSubTimer() : null,
+                        rawRealtime, "background partial", which, linePrefix);
+                linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), rawRealtime,
+                        "window", which, linePrefix);
+                linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_DRAW), rawRealtime,
+                        "draw", which, linePrefix);
+                sb.append(" realtime");
+                pw.println(sb.toString());
+                uidActivity = true;
+                countWakelock++;
+
+                totalFullWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_FULL),
+                        rawRealtime, which);
+                totalPartialWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_PARTIAL),
+                        rawRealtime, which);
+                totalWindowWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_WINDOW),
+                        rawRealtime, which);
+                totalDrawWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_DRAW),
+                        rawRealtime, which);
+            }
+            if (countWakelock > 1) {
+                // get unpooled partial wakelock quantities (unlike totalPartialWakelock, which is
+                // pooled and therefore just a lower bound)
+                long actualTotalPartialWakelock = 0;
+                long actualBgPartialWakelock = 0;
+                if (u.getAggregatedPartialWakelockTimer() != null) {
+                    final Timer aggTimer = u.getAggregatedPartialWakelockTimer();
+                    // Convert from microseconds to milliseconds with rounding
+                    actualTotalPartialWakelock =
+                            aggTimer.getTotalDurationMsLocked(rawRealtimeMs);
+                    final Timer bgAggTimer = aggTimer.getSubTimer();
+                    actualBgPartialWakelock = bgAggTimer != null ?
+                            bgAggTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                }
+
+                if (actualTotalPartialWakelock != 0 || actualBgPartialWakelock != 0 ||
+                        totalFullWakelock != 0 || totalPartialWakelock != 0 ||
+                        totalWindowWakelock != 0) {
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    TOTAL wake: ");
+                    boolean needComma = false;
+                    if (totalFullWakelock != 0) {
+                        needComma = true;
+                        formatTimeMs(sb, totalFullWakelock);
+                        sb.append("full");
+                    }
+                    if (totalPartialWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, totalPartialWakelock);
+                        sb.append("blamed partial");
+                    }
+                    if (actualTotalPartialWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, actualTotalPartialWakelock);
+                        sb.append("actual partial");
+                    }
+                    if (actualBgPartialWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, actualBgPartialWakelock);
+                        sb.append("actual background partial");
+                    }
+                    if (totalWindowWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, totalWindowWakelock);
+                        sb.append("window");
+                    }
+                    if (totalDrawWakelock != 0) {
+                        if (needComma) {
+                            sb.append(",");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, totalDrawWakelock);
+                        sb.append("draw");
+                    }
+                    sb.append(" realtime");
+                    pw.println(sb.toString());
+                }
+            }
+
+            // Calculate multicast wakelock stats
+            final Timer mcTimer = u.getMulticastWakelockStats();
+            if (mcTimer != null) {
+                final long multicastWakeLockTimeMicros = mcTimer.getTotalTimeLocked(rawRealtime, which);
+                final int multicastWakeLockCount = mcTimer.getCountLocked(which);
+
+                if (multicastWakeLockTimeMicros > 0) {
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    WiFi Multicast Wakelock");
+                    sb.append(" count = ");
+                    sb.append(multicastWakeLockCount);
+                    sb.append(" time = ");
+                    formatTimeMsNoSpace(sb, (multicastWakeLockTimeMicros + 500) / 1000);
+                    pw.println(sb.toString());
+                }
+            }
+
+            final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+            for (int isy=syncs.size()-1; isy>=0; isy--) {
+                final Timer timer = syncs.valueAt(isy);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTime = bgTimer != null ?
+                        bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1;
+                final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Sync ");
+                sb.append(syncs.keyAt(isy));
+                sb.append(": ");
+                if (totalTime != 0) {
+                    formatTimeMs(sb, totalTime);
+                    sb.append("realtime (");
+                    sb.append(count);
+                    sb.append(" times)");
+                    if (bgTime > 0) {
+                        sb.append(", ");
+                        formatTimeMs(sb, bgTime);
+                        sb.append("background (");
+                        sb.append(bgCount);
+                        sb.append(" times)");
+                    }
+                } else {
+                    sb.append("(not used)");
+                }
+                pw.println(sb.toString());
+                uidActivity = true;
+            }
+
+            final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+            for (int ij=jobs.size()-1; ij>=0; ij--) {
+                final Timer timer = jobs.valueAt(ij);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTime = bgTimer != null ?
+                        bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1;
+                final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Job ");
+                sb.append(jobs.keyAt(ij));
+                sb.append(": ");
+                if (totalTime != 0) {
+                    formatTimeMs(sb, totalTime);
+                    sb.append("realtime (");
+                    sb.append(count);
+                    sb.append(" times)");
+                    if (bgTime > 0) {
+                        sb.append(", ");
+                        formatTimeMs(sb, bgTime);
+                        sb.append("background (");
+                        sb.append(bgCount);
+                        sb.append(" times)");
+                    }
+                } else {
+                    sb.append("(not used)");
+                }
+                pw.println(sb.toString());
+                uidActivity = true;
+            }
+
+            final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
+            for (int ic=completions.size()-1; ic>=0; ic--) {
+                SparseIntArray types = completions.valueAt(ic);
+                if (types != null) {
+                    pw.print(prefix);
+                    pw.print("    Job Completions ");
+                    pw.print(completions.keyAt(ic));
+                    pw.print(":");
+                    for (int it=0; it<types.size(); it++) {
+                        pw.print(" ");
+                        pw.print(JobParameters.getInternalReasonCodeDescription(types.keyAt(it)));
+                        pw.print("(");
+                        pw.print(types.valueAt(it));
+                        pw.print("x)");
+                    }
+                    pw.println();
+                }
+            }
+
+            u.getDeferredJobsLineLocked(sb, which);
+            if (sb.length() > 0) {
+                pw.print("    Jobs deferred on launch "); pw.println(sb.toString());
+            }
+
+            uidActivity |= printTimer(pw, sb, u.getFlashlightTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Flashlight");
+            uidActivity |= printTimer(pw, sb, u.getCameraTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Camera");
+            uidActivity |= printTimer(pw, sb, u.getVideoTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Video");
+            uidActivity |= printTimer(pw, sb, u.getAudioTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Audio");
+
+            final SparseArray<? extends Uid.Sensor> sensors = u.getSensorStats();
+            final int NSE = sensors.size();
+            for (int ise=0; ise<NSE; ise++) {
+                final Uid.Sensor se = sensors.valueAt(ise);
+                final int sensorNumber = sensors.keyAt(ise);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Sensor ");
+                int handle = se.getHandle();
+                if (handle == Uid.Sensor.GPS) {
+                    sb.append("GPS");
+                } else {
+                    sb.append(handle);
+                }
+                sb.append(": ");
+
+                final Timer timer = se.getSensorTime();
+                if (timer != null) {
+                    // Convert from microseconds to milliseconds with rounding
+                    final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
+                            / 1000;
+                    final int count = timer.getCountLocked(which);
+                    final Timer bgTimer = se.getSensorBackgroundTime();
+                    final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : 0;
+                    // 'actualTime' are unpooled and always since reset (regardless of 'which')
+                    final long actualTime = timer.getTotalDurationMsLocked(rawRealtimeMs);
+                    final long bgActualTime = bgTimer != null ?
+                            bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+
+                    //timer.logState();
+                    if (totalTime != 0) {
+                        if (actualTime != totalTime) {
+                            formatTimeMs(sb, totalTime);
+                            sb.append("blamed realtime, ");
+                        }
+
+                        formatTimeMs(sb, actualTime); // since reset, regardless of 'which'
+                        sb.append("realtime (");
+                        sb.append(count);
+                        sb.append(" times)");
+
+                        if (bgActualTime != 0 || bgCount > 0) {
+                            sb.append(", ");
+                            formatTimeMs(sb, bgActualTime); // since reset, regardless of 'which'
+                            sb.append("background (");
+                            sb.append(bgCount);
+                            sb.append(" times)");
+                        }
+                    } else {
+                        sb.append("(not used)");
+                    }
+                } else {
+                    sb.append("(not used)");
+                }
+
+                pw.println(sb.toString());
+                uidActivity = true;
+            }
+
+            uidActivity |= printTimer(pw, sb, u.getVibratorOnTimer(), rawRealtime, which, prefix,
+                    "Vibrator");
+            uidActivity |= printTimer(pw, sb, u.getForegroundActivityTimer(), rawRealtime, which,
+                    prefix, "Foreground activities");
+            uidActivity |= printTimer(pw, sb, u.getForegroundServiceTimer(), rawRealtime, which,
+                    prefix, "Foreground services");
+
+            long totalStateTime = 0;
+            for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
+                long time = u.getProcessStateTime(ips, rawRealtime, which);
+                if (time > 0) {
+                    totalStateTime += time;
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    ");
+                    sb.append(Uid.PROCESS_STATE_NAMES[ips]);
+                    sb.append(" for: ");
+                    formatTimeMs(sb, (time + 500) / 1000);
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+            if (totalStateTime > 0) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Total running: ");
+                formatTimeMs(sb, (totalStateTime + 500) / 1000);
+                pw.println(sb.toString());
+            }
+
+            final long userCpuTimeUs = u.getUserCpuTimeUs(which);
+            final long systemCpuTimeUs = u.getSystemCpuTimeUs(which);
+            if (userCpuTimeUs > 0 || systemCpuTimeUs > 0) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Total cpu time: u=");
+                formatTimeMs(sb, userCpuTimeUs / 1000);
+                sb.append("s=");
+                formatTimeMs(sb, systemCpuTimeUs / 1000);
+                pw.println(sb.toString());
+            }
+
+            final long[] cpuFreqTimes = u.getCpuFreqTimes(which);
+            if (cpuFreqTimes != null) {
+                sb.setLength(0);
+                sb.append("    Total cpu time per freq:");
+                for (int i = 0; i < cpuFreqTimes.length; ++i) {
+                    sb.append(' ').append(cpuFreqTimes[i]);
+                }
+                pw.println(sb.toString());
+            }
+            final long[] screenOffCpuFreqTimes = u.getScreenOffCpuFreqTimes(which);
+            if (screenOffCpuFreqTimes != null) {
+                sb.setLength(0);
+                sb.append("    Total screen-off cpu time per freq:");
+                for (int i = 0; i < screenOffCpuFreqTimes.length; ++i) {
+                    sb.append(' ').append(screenOffCpuFreqTimes[i]);
+                }
+                pw.println(sb.toString());
+            }
+
+            final long[] timesInFreqMs = new long[getCpuFreqCount()];
+            for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
+                if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
+                    sb.setLength(0);
+                    sb.append("    Cpu times per freq at state ")
+                            .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
+                    for (int i = 0; i < timesInFreqMs.length; ++i) {
+                        sb.append(" ").append(timesInFreqMs[i]);
+                    }
+                    pw.println(sb.toString());
+                }
+
+                if (u.getScreenOffCpuFreqTimes(timesInFreqMs, procState)) {
+                    sb.setLength(0);
+                    sb.append("   Screen-off cpu times per freq at state ")
+                            .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
+                    for (int i = 0; i < timesInFreqMs.length; ++i) {
+                        sb.append(" ").append(timesInFreqMs[i]);
+                    }
+                    pw.println(sb.toString());
+                }
+            }
+
+            final ArrayMap<String, ? extends Uid.Proc> processStats
+                    = u.getProcessStats();
+            for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
+                final Uid.Proc ps = processStats.valueAt(ipr);
+                long userTime;
+                long systemTime;
+                long foregroundTime;
+                int starts;
+                int numExcessive;
+
+                userTime = ps.getUserTime(which);
+                systemTime = ps.getSystemTime(which);
+                foregroundTime = ps.getForegroundTime(which);
+                starts = ps.getStarts(which);
+                final int numCrashes = ps.getNumCrashes(which);
+                final int numAnrs = ps.getNumAnrs(which);
+                numExcessive = which == STATS_SINCE_CHARGED
+                        ? ps.countExcessivePowers() : 0;
+
+                if (userTime != 0 || systemTime != 0 || foregroundTime != 0 || starts != 0
+                        || numExcessive != 0 || numCrashes != 0 || numAnrs != 0) {
+                    sb.setLength(0);
+                    sb.append(prefix); sb.append("    Proc ");
+                            sb.append(processStats.keyAt(ipr)); sb.append(":\n");
+                    sb.append(prefix); sb.append("      CPU: ");
+                            formatTimeMs(sb, userTime); sb.append("usr + ");
+                            formatTimeMs(sb, systemTime); sb.append("krn ; ");
+                            formatTimeMs(sb, foregroundTime); sb.append("fg");
+                    if (starts != 0 || numCrashes != 0 || numAnrs != 0) {
+                        sb.append("\n"); sb.append(prefix); sb.append("      ");
+                        boolean hasOne = false;
+                        if (starts != 0) {
+                            hasOne = true;
+                            sb.append(starts); sb.append(" starts");
+                        }
+                        if (numCrashes != 0) {
+                            if (hasOne) {
+                                sb.append(", ");
+                            }
+                            hasOne = true;
+                            sb.append(numCrashes); sb.append(" crashes");
+                        }
+                        if (numAnrs != 0) {
+                            if (hasOne) {
+                                sb.append(", ");
+                            }
+                            sb.append(numAnrs); sb.append(" anrs");
+                        }
+                    }
+                    pw.println(sb.toString());
+                    for (int e=0; e<numExcessive; e++) {
+                        Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e);
+                        if (ew != null) {
+                            pw.print(prefix); pw.print("      * Killed for ");
+                                    if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) {
+                                        pw.print("cpu");
+                                    } else {
+                                        pw.print("unknown");
+                                    }
+                                    pw.print(" use: ");
+                                    TimeUtils.formatDuration(ew.usedTime, pw);
+                                    pw.print(" over ");
+                                    TimeUtils.formatDuration(ew.overTime, pw);
+                                    if (ew.overTime != 0) {
+                                        pw.print(" (");
+                                        pw.print((ew.usedTime*100)/ew.overTime);
+                                        pw.println("%)");
+                                    }
+                        }
+                    }
+                    uidActivity = true;
+                }
+            }
+
+            final ArrayMap<String, ? extends Uid.Pkg> packageStats
+                    = u.getPackageStats();
+            for (int ipkg=packageStats.size()-1; ipkg>=0; ipkg--) {
+                pw.print(prefix); pw.print("    Apk "); pw.print(packageStats.keyAt(ipkg));
+                pw.println(":");
+                boolean apkActivity = false;
+                final Uid.Pkg ps = packageStats.valueAt(ipkg);
+                final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+                for (int iwa=alarms.size()-1; iwa>=0; iwa--) {
+                    pw.print(prefix); pw.print("      Wakeup alarm ");
+                            pw.print(alarms.keyAt(iwa)); pw.print(": ");
+                            pw.print(alarms.valueAt(iwa).getCountLocked(which));
+                            pw.println(" times");
+                    apkActivity = true;
+                }
+                final ArrayMap<String, ? extends  Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+                for (int isvc=serviceStats.size()-1; isvc>=0; isvc--) {
+                    final Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+                    final long startTime = ss.getStartTime(batteryUptime, which);
+                    final int starts = ss.getStarts(which);
+                    final int launches = ss.getLaunches(which);
+                    if (startTime != 0 || starts != 0 || launches != 0) {
+                        sb.setLength(0);
+                        sb.append(prefix); sb.append("      Service ");
+                                sb.append(serviceStats.keyAt(isvc)); sb.append(":\n");
+                        sb.append(prefix); sb.append("        Created for: ");
+                                formatTimeMs(sb, startTime / 1000);
+                                sb.append("uptime\n");
+                        sb.append(prefix); sb.append("        Starts: ");
+                                sb.append(starts);
+                                sb.append(", launches: "); sb.append(launches);
+                        pw.println(sb.toString());
+                        apkActivity = true;
+                    }
+                }
+                if (!apkActivity) {
+                    pw.print(prefix); pw.println("      (nothing executed)");
+                }
+                uidActivity = true;
+            }
+            if (!uidActivity) {
+                pw.print(prefix); pw.println("    (nothing executed)");
+            }
+        }
+    }
+
+    static void printBitDescriptions(StringBuilder sb, int oldval, int newval,
+            HistoryTag wakelockTag, BitDescription[] descriptions, boolean longNames) {
+        int diff = oldval ^ newval;
+        if (diff == 0) return;
+        boolean didWake = false;
+        for (int i=0; i<descriptions.length; i++) {
+            BitDescription bd = descriptions[i];
+            if ((diff&bd.mask) != 0) {
+                sb.append(longNames ? " " : ",");
+                if (bd.shift < 0) {
+                    sb.append((newval & bd.mask) != 0 ? "+" : "-");
+                    sb.append(longNames ? bd.name : bd.shortName);
+                    if (bd.mask == HistoryItem.STATE_WAKE_LOCK_FLAG && wakelockTag != null) {
+                        didWake = true;
+                        sb.append("=");
+                        if (longNames) {
+                            UserHandle.formatUid(sb, wakelockTag.uid);
+                            sb.append(":\"");
+                            sb.append(wakelockTag.string);
+                            sb.append("\"");
+                        } else {
+                            sb.append(wakelockTag.poolIdx);
+                        }
+                    }
+                } else {
+                    sb.append(longNames ? bd.name : bd.shortName);
+                    sb.append("=");
+                    int val = (newval&bd.mask)>>bd.shift;
+                    if (bd.values != null && val >= 0 && val < bd.values.length) {
+                        sb.append(longNames ? bd.values[val] : bd.shortValues[val]);
+                    } else {
+                        sb.append(val);
+                    }
+                }
+            }
+        }
+        if (!didWake && wakelockTag != null) {
+            sb.append(longNames ? " wake_lock=" : ",w=");
+            if (longNames) {
+                UserHandle.formatUid(sb, wakelockTag.uid);
+                sb.append(":\"");
+                sb.append(wakelockTag.string);
+                sb.append("\"");
+            } else {
+                sb.append(wakelockTag.poolIdx);
+            }
+        }
+    }
+
+    public void prepareForDumpLocked() {
+        // We don't need to require subclasses implement this.
+    }
+
+    public static class HistoryPrinter {
+        int oldState = 0;
+        int oldState2 = 0;
+        int oldLevel = -1;
+        int oldStatus = -1;
+        int oldHealth = -1;
+        int oldPlug = -1;
+        int oldTemp = -1;
+        int oldVolt = -1;
+        int oldChargeMAh = -1;
+        double oldModemRailChargeMah = -1;
+        double oldWifiRailChargeMah = -1;
+        long lastTime = -1;
+
+        void reset() {
+            oldState = oldState2 = 0;
+            oldLevel = -1;
+            oldStatus = -1;
+            oldHealth = -1;
+            oldPlug = -1;
+            oldTemp = -1;
+            oldVolt = -1;
+            oldChargeMAh = -1;
+            oldModemRailChargeMah = -1;
+            oldWifiRailChargeMah = -1;
+        }
+
+        public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
+                boolean verbose) {
+            pw.print(printNextItem(rec, baseTime, checkin, verbose));
+        }
+
+        /** Print the next history item to proto. */
+        public void printNextItem(ProtoOutputStream proto, HistoryItem rec, long baseTime,
+                boolean verbose) {
+            String item = printNextItem(rec, baseTime, true, verbose);
+            for (String line : item.split("\n")) {
+                proto.write(BatteryStatsServiceDumpHistoryProto.CSV_LINES, line);
+            }
+        }
+
+        private String printNextItem(HistoryItem rec, long baseTime, boolean checkin,
+                boolean verbose) {
+            StringBuilder item = new StringBuilder();
+
+            if (rec.cpuUsageDetails != null
+                    && rec.cpuUsageDetails.cpuBracketDescriptions != null
+                    && checkin) {
+                String[] descriptions = rec.cpuUsageDetails.cpuBracketDescriptions;
+                for (int bracket = 0; bracket < descriptions.length; bracket++) {
+                    item.append(BATTERY_STATS_CHECKIN_VERSION);
+                    item.append(',');
+                    item.append(HISTORY_DATA);
+                    item.append(",0,XB,");
+                    item.append(descriptions.length);
+                    item.append(',');
+                    item.append(bracket);
+                    item.append(',');
+                    item.append(descriptions[bracket]);
+                    item.append("\n");
+                }
+            }
+
+            if (!checkin) {
+                item.append("  ");
+                TimeUtils.formatDuration(
+                        rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                item.append(" (");
+                item.append(rec.numReadInts);
+                item.append(") ");
+            } else {
+                item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
+                item.append(HISTORY_DATA); item.append(',');
+                if (lastTime < 0) {
+                    item.append(rec.time - baseTime);
+                } else {
+                    item.append(rec.time - lastTime);
+                }
+                lastTime = rec.time;
+            }
+            if (rec.cmd == HistoryItem.CMD_START) {
+                if (checkin) {
+                    item.append(":");
+                }
+                item.append("START\n");
+                reset();
+            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                    || rec.cmd == HistoryItem.CMD_RESET) {
+                if (checkin) {
+                    item.append(":");
+                }
+                if (rec.cmd == HistoryItem.CMD_RESET) {
+                    item.append("RESET:");
+                    reset();
+                }
+                item.append("TIME:");
+                if (checkin) {
+                    item.append(rec.currentTime);
+                    item.append("\n");
+                } else {
+                    item.append(" ");
+                    item.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                            rec.currentTime).toString());
+                    item.append("\n");
+                }
+            } else if (rec.cmd == HistoryItem.CMD_SHUTDOWN) {
+                if (checkin) {
+                    item.append(":");
+                }
+                item.append("SHUTDOWN\n");
+            } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+                if (checkin) {
+                    item.append(":");
+                }
+                item.append("*OVERFLOW*\n");
+            } else {
+                if (!checkin) {
+                    if (rec.batteryLevel < 10) item.append("00");
+                    else if (rec.batteryLevel < 100) item.append("0");
+                    item.append(rec.batteryLevel);
+                    if (verbose) {
+                        item.append(" ");
+                        if (rec.states < 0) ;
+                        else if (rec.states < 0x10) item.append("0000000");
+                        else if (rec.states < 0x100) item.append("000000");
+                        else if (rec.states < 0x1000) item.append("00000");
+                        else if (rec.states < 0x10000) item.append("0000");
+                        else if (rec.states < 0x100000) item.append("000");
+                        else if (rec.states < 0x1000000) item.append("00");
+                        else if (rec.states < 0x10000000) item.append("0");
+                        item.append(Integer.toHexString(rec.states));
+                    }
+                } else {
+                    if (oldLevel != rec.batteryLevel) {
+                        oldLevel = rec.batteryLevel;
+                        item.append(",Bl="); item.append(rec.batteryLevel);
+                    }
+                }
+                if (oldStatus != rec.batteryStatus) {
+                    oldStatus = rec.batteryStatus;
+                    item.append(checkin ? ",Bs=" : " status=");
+                    switch (oldStatus) {
+                        case BatteryManager.BATTERY_STATUS_UNKNOWN:
+                            item.append(checkin ? "?" : "unknown");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_CHARGING:
+                            item.append(checkin ? "c" : "charging");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_DISCHARGING:
+                            item.append(checkin ? "d" : "discharging");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+                            item.append(checkin ? "n" : "not-charging");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_FULL:
+                            item.append(checkin ? "f" : "full");
+                            break;
+                        default:
+                            item.append(oldStatus);
+                            break;
+                    }
+                }
+                if (oldHealth != rec.batteryHealth) {
+                    oldHealth = rec.batteryHealth;
+                    item.append(checkin ? ",Bh=" : " health=");
+                    switch (oldHealth) {
+                        case BatteryManager.BATTERY_HEALTH_UNKNOWN:
+                            item.append(checkin ? "?" : "unknown");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_GOOD:
+                            item.append(checkin ? "g" : "good");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_OVERHEAT:
+                            item.append(checkin ? "h" : "overheat");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_DEAD:
+                            item.append(checkin ? "d" : "dead");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
+                            item.append(checkin ? "v" : "over-voltage");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
+                            item.append(checkin ? "f" : "failure");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_COLD:
+                            item.append(checkin ? "c" : "cold");
+                            break;
+                        default:
+                            item.append(oldHealth);
+                            break;
+                    }
+                }
+                if (oldPlug != rec.batteryPlugType) {
+                    oldPlug = rec.batteryPlugType;
+                    item.append(checkin ? ",Bp=" : " plug=");
+                    switch (oldPlug) {
+                        case 0:
+                            item.append(checkin ? "n" : "none");
+                            break;
+                        case BatteryManager.BATTERY_PLUGGED_AC:
+                            item.append(checkin ? "a" : "ac");
+                            break;
+                        case BatteryManager.BATTERY_PLUGGED_USB:
+                            item.append(checkin ? "u" : "usb");
+                            break;
+                        case BatteryManager.BATTERY_PLUGGED_WIRELESS:
+                            item.append(checkin ? "w" : "wireless");
+                            break;
+                        default:
+                            item.append(oldPlug);
+                            break;
+                    }
+                }
+                if (oldTemp != rec.batteryTemperature) {
+                    oldTemp = rec.batteryTemperature;
+                    item.append(checkin ? ",Bt=" : " temp=");
+                    item.append(oldTemp);
+                }
+                if (oldVolt != rec.batteryVoltage) {
+                    oldVolt = rec.batteryVoltage;
+                    item.append(checkin ? ",Bv=" : " volt=");
+                    item.append(oldVolt);
+                }
+                final int chargeMAh = rec.batteryChargeUah / 1000;
+                if (oldChargeMAh != chargeMAh) {
+                    oldChargeMAh = chargeMAh;
+                    item.append(checkin ? ",Bcc=" : " charge=");
+                    item.append(oldChargeMAh);
+                }
+                if (oldModemRailChargeMah != rec.modemRailChargeMah) {
+                    oldModemRailChargeMah = rec.modemRailChargeMah;
+                    item.append(checkin ? ",Mrc=" : " modemRailChargemAh=");
+                    item.append(new DecimalFormat("#.##").format(oldModemRailChargeMah));
+                }
+                if (oldWifiRailChargeMah != rec.wifiRailChargeMah) {
+                    oldWifiRailChargeMah = rec.wifiRailChargeMah;
+                    item.append(checkin ? ",Wrc=" : " wifiRailChargemAh=");
+                    item.append(new DecimalFormat("#.##").format(oldWifiRailChargeMah));
+                }
+                printBitDescriptions(item, oldState, rec.states, rec.wakelockTag,
+                        HISTORY_STATE_DESCRIPTIONS, !checkin);
+                printBitDescriptions(item, oldState2, rec.states2, null,
+                        HISTORY_STATE2_DESCRIPTIONS, !checkin);
+                if (rec.wakeReasonTag != null) {
+                    if (checkin) {
+                        item.append(",wr=");
+                        item.append(rec.wakeReasonTag.poolIdx);
+                    } else {
+                        item.append(" wake_reason=");
+                        item.append(rec.wakeReasonTag.uid);
+                        item.append(":\"");
+                        item.append(rec.wakeReasonTag.string);
+                        item.append("\"");
+                    }
+                }
+                if (rec.eventCode != HistoryItem.EVENT_NONE) {
+                    item.append(checkin ? "," : " ");
+                    if ((rec.eventCode&HistoryItem.EVENT_FLAG_START) != 0) {
+                        item.append("+");
+                    } else if ((rec.eventCode&HistoryItem.EVENT_FLAG_FINISH) != 0) {
+                        item.append("-");
+                    }
+                    String[] eventNames = checkin ? HISTORY_EVENT_CHECKIN_NAMES
+                            : HISTORY_EVENT_NAMES;
+                    int idx = rec.eventCode & ~(HistoryItem.EVENT_FLAG_START
+                            | HistoryItem.EVENT_FLAG_FINISH);
+                    if (idx >= 0 && idx < eventNames.length) {
+                        item.append(eventNames[idx]);
+                    } else {
+                        item.append(checkin ? "Ev" : "event");
+                        item.append(idx);
+                    }
+                    item.append("=");
+                    if (checkin) {
+                        item.append(rec.eventTag.poolIdx);
+                    } else {
+                        item.append(HISTORY_EVENT_INT_FORMATTERS[idx]
+                                .applyAsString(rec.eventTag.uid));
+                        item.append(":\"");
+                        item.append(rec.eventTag.string);
+                        item.append("\"");
+                    }
+                }
+                boolean firstExtension = true;
+                if (rec.energyConsumerDetails != null) {
+                    firstExtension = false;
+                    if (!checkin) {
+                        item.append(" ext=energy:");
+                        item.append(rec.energyConsumerDetails);
+                    } else {
+                        item.append(",XE");
+                        for (int i = 0; i < rec.energyConsumerDetails.consumers.length; i++) {
+                            if (rec.energyConsumerDetails.chargeUC[i] != POWER_DATA_UNAVAILABLE) {
+                                item.append(',');
+                                item.append(rec.energyConsumerDetails.consumers[i].name);
+                                item.append('=');
+                                item.append(rec.energyConsumerDetails.chargeUC[i]);
+                            }
+                        }
+                    }
+                }
+                if (rec.cpuUsageDetails != null) {
+                    if (!checkin) {
+                        if (!firstExtension) {
+                            item.append("\n                ");
+                        }
+                        String[] descriptions = rec.cpuUsageDetails.cpuBracketDescriptions;
+                        if (descriptions != null) {
+                            for (int bracket = 0; bracket < descriptions.length; bracket++) {
+                                item.append(" ext=cpu-bracket:");
+                                item.append(bracket);
+                                item.append(":");
+                                item.append(descriptions[bracket]);
+                                item.append("\n                ");
+                            }
+                        }
+                        item.append(" ext=cpu:");
+                        item.append(rec.cpuUsageDetails);
+                    } else {
+                        if (!firstExtension) {
+                            item.append('\n');
+                            item.append(BATTERY_STATS_CHECKIN_VERSION);
+                            item.append(',');
+                            item.append(HISTORY_DATA);
+                            item.append(",0");
+                        }
+                        item.append(",XC,");
+                        item.append(rec.cpuUsageDetails.uid);
+                        for (int i = 0; i < rec.cpuUsageDetails.cpuUsageMs.length; i++) {
+                            item.append(',');
+                            item.append(rec.cpuUsageDetails.cpuUsageMs[i]);
+                        }
+                    }
+                    firstExtension = false;
+                }
+                item.append("\n");
+                if (rec.stepDetails != null) {
+                    if (!checkin) {
+                        item.append("                 Details: cpu=");
+                        item.append(rec.stepDetails.userTime);
+                        item.append("u+");
+                        item.append(rec.stepDetails.systemTime);
+                        item.append("s");
+                        if (rec.stepDetails.appCpuUid1 >= 0) {
+                            item.append(" (");
+                            printStepCpuUidDetails(item, rec.stepDetails.appCpuUid1,
+                                    rec.stepDetails.appCpuUTime1, rec.stepDetails.appCpuSTime1);
+                            if (rec.stepDetails.appCpuUid2 >= 0) {
+                                item.append(", ");
+                                printStepCpuUidDetails(item, rec.stepDetails.appCpuUid2,
+                                        rec.stepDetails.appCpuUTime2, rec.stepDetails.appCpuSTime2);
+                            }
+                            if (rec.stepDetails.appCpuUid3 >= 0) {
+                                item.append(", ");
+                                printStepCpuUidDetails(item, rec.stepDetails.appCpuUid3,
+                                        rec.stepDetails.appCpuUTime3, rec.stepDetails.appCpuSTime3);
+                            }
+                            item.append(')');
+                        }
+                        item.append("\n");
+                        item.append("                          /proc/stat=");
+                        item.append(rec.stepDetails.statUserTime);
+                        item.append(" usr, ");
+                        item.append(rec.stepDetails.statSystemTime);
+                        item.append(" sys, ");
+                        item.append(rec.stepDetails.statIOWaitTime);
+                        item.append(" io, ");
+                        item.append(rec.stepDetails.statIrqTime);
+                        item.append(" irq, ");
+                        item.append(rec.stepDetails.statSoftIrqTime);
+                        item.append(" sirq, ");
+                        item.append(rec.stepDetails.statIdlTime);
+                        item.append(" idle");
+                        int totalRun = rec.stepDetails.statUserTime + rec.stepDetails.statSystemTime
+                                + rec.stepDetails.statIOWaitTime + rec.stepDetails.statIrqTime
+                                + rec.stepDetails.statSoftIrqTime;
+                        int total = totalRun + rec.stepDetails.statIdlTime;
+                        if (total > 0) {
+                            item.append(" (");
+                            float perc = ((float)totalRun) / ((float)total) * 100;
+                            item.append(String.format("%.1f%%", perc));
+                            item.append(" of ");
+                            StringBuilder sb = new StringBuilder(64);
+                            formatTimeMsNoSpace(sb, total*10);
+                            item.append(sb);
+                            item.append(")");
+                        }
+
+                        item.append(", SubsystemPowerState ");
+                        item.append(rec.stepDetails.statSubsystemPowerState);
+                        item.append("\n");
+                    } else {
+                        item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
+                        item.append(HISTORY_DATA); item.append(",0,Dcpu=");
+                        item.append(rec.stepDetails.userTime);
+                        item.append(":");
+                        item.append(rec.stepDetails.systemTime);
+                        if (rec.stepDetails.appCpuUid1 >= 0) {
+                            printStepCpuUidCheckinDetails(item, rec.stepDetails.appCpuUid1,
+                                    rec.stepDetails.appCpuUTime1, rec.stepDetails.appCpuSTime1);
+                            if (rec.stepDetails.appCpuUid2 >= 0) {
+                                printStepCpuUidCheckinDetails(item, rec.stepDetails.appCpuUid2,
+                                        rec.stepDetails.appCpuUTime2, rec.stepDetails.appCpuSTime2);
+                            }
+                            if (rec.stepDetails.appCpuUid3 >= 0) {
+                                printStepCpuUidCheckinDetails(item, rec.stepDetails.appCpuUid3,
+                                        rec.stepDetails.appCpuUTime3, rec.stepDetails.appCpuSTime3);
+                            }
+                        }
+                        item.append("\n");
+                        item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
+                        item.append(HISTORY_DATA); item.append(",0,Dpst=");
+                        item.append(rec.stepDetails.statUserTime);
+                        item.append(',');
+                        item.append(rec.stepDetails.statSystemTime);
+                        item.append(',');
+                        item.append(rec.stepDetails.statIOWaitTime);
+                        item.append(',');
+                        item.append(rec.stepDetails.statIrqTime);
+                        item.append(',');
+                        item.append(rec.stepDetails.statSoftIrqTime);
+                        item.append(',');
+                        item.append(rec.stepDetails.statIdlTime);
+                        item.append(',');
+
+                        if (rec.stepDetails.statSubsystemPowerState != null) {
+                            item.append(rec.stepDetails.statSubsystemPowerState);
+                        }
+                        item.append("\n");
+                    }
+                }
+                oldState = rec.states;
+                oldState2 = rec.states2;
+                // Clear High Tx Power Flag for volta positioning
+                if ((rec.states2 & HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG) != 0) {
+                    rec.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+                }
+            }
+            return item.toString();
+        }
+
+        private void printStepCpuUidDetails(StringBuilder sb, int uid, int utime, int stime) {
+            UserHandle.formatUid(sb, uid);
+            sb.append("=");
+            sb.append(utime);
+            sb.append("u+");
+            sb.append(stime);
+            sb.append("s");
+        }
+
+        private void printStepCpuUidCheckinDetails(StringBuilder sb, int uid, int utime,
+                int stime) {
+            sb.append('/');
+            sb.append(uid);
+            sb.append(":");
+            sb.append(utime);
+            sb.append(":");
+            sb.append(stime);
+        }
+    }
+
+    private void printSizeValue(PrintWriter pw, long size) {
+        float result = size;
+        String suffix = "";
+        if (result >= 10*1024) {
+            suffix = "KB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "MB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "GB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "TB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "PB";
+            result = result / 1024;
+        }
+        pw.print((int)result);
+        pw.print(suffix);
+    }
+
+    private static boolean dumpTimeEstimate(PrintWriter pw, String label1, String label2,
+            String label3, long estimatedTime) {
+        if (estimatedTime < 0) {
+            return false;
+        }
+        pw.print(label1);
+        pw.print(label2);
+        pw.print(label3);
+        StringBuilder sb = new StringBuilder(64);
+        formatTimeMs(sb, estimatedTime);
+        pw.print(sb);
+        pw.println();
+        return true;
+    }
+
+    private static boolean dumpDurationSteps(PrintWriter pw, String prefix, String header,
+            LevelStepTracker steps, boolean checkin) {
+        if (steps == null) {
+            return false;
+        }
+        int count = steps.mNumStepDurations;
+        if (count <= 0) {
+            return false;
+        }
+        if (!checkin) {
+            pw.println(header);
+        }
+        String[] lineArgs = new String[5];
+        for (int i=0; i<count; i++) {
+            long duration = steps.getDurationAt(i);
+            int level = steps.getLevelAt(i);
+            long initMode = steps.getInitModeAt(i);
+            long modMode = steps.getModModeAt(i);
+            if (checkin) {
+                lineArgs[0] = Long.toString(duration);
+                lineArgs[1] = Integer.toString(level);
+                if ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
+                    switch ((int)(initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                        case Display.STATE_OFF: lineArgs[2] = "s-"; break;
+                        case Display.STATE_ON: lineArgs[2] = "s+"; break;
+                        case Display.STATE_DOZE: lineArgs[2] = "sd"; break;
+                        case Display.STATE_DOZE_SUSPEND: lineArgs[2] = "sds"; break;
+                        default: lineArgs[2] = "?"; break;
+                    }
+                } else {
+                    lineArgs[2] = "";
+                }
+                if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) == 0) {
+                    lineArgs[3] = (initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0 ? "p+" : "p-";
+                } else {
+                    lineArgs[3] = "";
+                }
+                if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                    lineArgs[4] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-";
+                } else {
+                    lineArgs[4] = "";
+                }
+                dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
+            } else {
+                pw.print(prefix);
+                pw.print("#"); pw.print(i); pw.print(": ");
+                TimeUtils.formatDuration(duration, pw);
+                pw.print(" to "); pw.print(level);
+                boolean haveModes = false;
+                if ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
+                    pw.print(" (");
+                    switch ((int)(initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                        case Display.STATE_OFF: pw.print("screen-off"); break;
+                        case Display.STATE_ON: pw.print("screen-on"); break;
+                        case Display.STATE_DOZE: pw.print("screen-doze"); break;
+                        case Display.STATE_DOZE_SUSPEND: pw.print("screen-doze-suspend"); break;
+                        default: pw.print("screen-?"); break;
+                    }
+                    haveModes = true;
+                }
+                if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) == 0) {
+                    pw.print(haveModes ? ", " : " (");
+                    pw.print((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0
+                            ? "power-save-on" : "power-save-off");
+                    haveModes = true;
+                }
+                if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                    pw.print(haveModes ? ", " : " (");
+                    pw.print((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+                            ? "device-idle-on" : "device-idle-off");
+                    haveModes = true;
+                }
+                if (haveModes) {
+                    pw.print(")");
+                }
+                pw.println();
+            }
+        }
+        return true;
+    }
+
+    private static void dumpDurationSteps(ProtoOutputStream proto, long fieldId,
+            LevelStepTracker steps) {
+        if (steps == null) {
+            return;
+        }
+        int count = steps.mNumStepDurations;
+        for (int i = 0; i < count; ++i) {
+            long token = proto.start(fieldId);
+            proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i));
+            proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i));
+
+            final long initMode = steps.getInitModeAt(i);
+            final long modMode = steps.getModModeAt(i);
+
+            int ds = SystemProto.BatteryLevelStep.DS_MIXED;
+            if ((modMode & STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
+                switch ((int) (initMode & STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                    case Display.STATE_OFF:
+                        ds = SystemProto.BatteryLevelStep.DS_OFF;
+                        break;
+                    case Display.STATE_ON:
+                        ds = SystemProto.BatteryLevelStep.DS_ON;
+                        break;
+                    case Display.STATE_DOZE:
+                        ds = SystemProto.BatteryLevelStep.DS_DOZE;
+                        break;
+                    case Display.STATE_DOZE_SUSPEND:
+                        ds = SystemProto.BatteryLevelStep.DS_DOZE_SUSPEND;
+                        break;
+                    default:
+                        ds = SystemProto.BatteryLevelStep.DS_ERROR;
+                        break;
+                }
+            }
+            proto.write(SystemProto.BatteryLevelStep.DISPLAY_STATE, ds);
+
+            int psm = SystemProto.BatteryLevelStep.PSM_MIXED;
+            if ((modMode & STEP_LEVEL_MODE_POWER_SAVE) == 0) {
+                psm = (initMode & STEP_LEVEL_MODE_POWER_SAVE) != 0
+                    ? SystemProto.BatteryLevelStep.PSM_ON : SystemProto.BatteryLevelStep.PSM_OFF;
+            }
+            proto.write(SystemProto.BatteryLevelStep.POWER_SAVE_MODE, psm);
+
+            int im = SystemProto.BatteryLevelStep.IM_MIXED;
+            if ((modMode & STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                im = (initMode & STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+                    ? SystemProto.BatteryLevelStep.IM_ON : SystemProto.BatteryLevelStep.IM_OFF;
+            }
+            proto.write(SystemProto.BatteryLevelStep.IDLE_MODE, im);
+
+            proto.end(token);
+        }
+    }
+
+    public static final int DUMP_CHARGED_ONLY = 1<<1;
+    public static final int DUMP_DAILY_ONLY = 1<<2;
+    public static final int DUMP_HISTORY_ONLY = 1<<3;
+    public static final int DUMP_INCLUDE_HISTORY = 1<<4;
+    public static final int DUMP_VERBOSE = 1<<5;
+    public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6;
+
+    private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) {
+        synchronized (this) {
+            dumpHistoryTagPoolLocked(pw, checkin);
+        }
+
+        final HistoryPrinter hprinter = new HistoryPrinter();
+        long lastTime = -1;
+        long baseTime = -1;
+        boolean printed = false;
+        HistoryEventTracker tracker = null;
+        try (BatteryStatsHistoryIterator iterator = iterateBatteryStatsHistory()) {
+            HistoryItem rec;
+            while ((rec = iterator.next()) != null) {
+                try {
+                    lastTime = rec.time;
+                    if (baseTime < 0) {
+                        baseTime = lastTime;
+                    }
+                    if (rec.time >= histStart) {
+                        if (histStart >= 0 && !printed) {
+                            if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                                    || rec.cmd == HistoryItem.CMD_RESET
+                                    || rec.cmd == HistoryItem.CMD_START
+                                    || rec.cmd == HistoryItem.CMD_SHUTDOWN) {
+                                printed = true;
+                                hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                        (flags & DUMP_VERBOSE) != 0);
+                                rec.cmd = HistoryItem.CMD_UPDATE;
+                            } else if (rec.currentTime != 0) {
+                                printed = true;
+                                byte cmd = rec.cmd;
+                                rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+                                hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                        (flags & DUMP_VERBOSE) != 0);
+                                rec.cmd = cmd;
+                            }
+                            if (tracker != null) {
+                                if (rec.cmd != HistoryItem.CMD_UPDATE) {
+                                    hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                            (flags & DUMP_VERBOSE) != 0);
+                                    rec.cmd = HistoryItem.CMD_UPDATE;
+                                }
+                                int oldEventCode = rec.eventCode;
+                                HistoryTag oldEventTag = rec.eventTag;
+                                rec.eventTag = new HistoryTag();
+                                for (int i = 0; i < HistoryItem.EVENT_COUNT; i++) {
+                                    Map<String, SparseIntArray> active =
+                                            tracker.getStateForEvent(i);
+                                    if (active == null) {
+                                        continue;
+                                    }
+                                    for (Map.Entry<String, SparseIntArray> ent :
+                                            active.entrySet()) {
+                                        SparseIntArray uids = ent.getValue();
+                                        for (int j = 0; j < uids.size(); j++) {
+                                            rec.eventCode = i;
+                                            rec.eventTag.string = ent.getKey();
+                                            rec.eventTag.uid = uids.keyAt(j);
+                                            rec.eventTag.poolIdx = uids.valueAt(j);
+                                            hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                                    (flags & DUMP_VERBOSE) != 0);
+                                            rec.wakeReasonTag = null;
+                                            rec.wakelockTag = null;
+                                        }
+                                    }
+                                }
+                                rec.eventCode = oldEventCode;
+                                rec.eventTag = oldEventTag;
+                                tracker = null;
+                            }
+                        }
+                        hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                (flags & DUMP_VERBOSE) != 0);
+                    } else if (false/* && rec.eventCode != HistoryItem.EVENT_NONE */) {
+                        // This is an attempt to aggregate the previous state and generate
+                        // fake events to reflect that state at the point where we start
+                        // printing real events.  It doesn't really work right, so is turned off.
+                        if (tracker == null) {
+                            tracker = new HistoryEventTracker();
+                        }
+                        tracker.updateState(rec.eventCode, rec.eventTag.string,
+                                rec.eventTag.uid, rec.eventTag.poolIdx);
+                    }
+                } catch (Throwable t) {
+                    t.printStackTrace(pw);
+                    Slog.wtf(TAG, "Corrupted battery history", t);
+                    break;
+                }
+            }
+        }
+        if (histStart >= 0) {
+            commitCurrentHistoryBatchLocked();
+            pw.print(checkin ? "NEXT: " : "  NEXT: "); pw.println(lastTime+1);
+        }
+    }
+
+    private void dumpHistoryTagPoolLocked(PrintWriter pw, boolean checkin) {
+        if (checkin) {
+            for (int i = 0; i < getHistoryStringPoolSize(); i++) {
+                pw.print(BATTERY_STATS_CHECKIN_VERSION);
+                pw.print(',');
+                pw.print(HISTORY_STRING_POOL);
+                pw.print(',');
+                pw.print(i);
+                pw.print(",");
+                pw.print(getHistoryTagPoolUid(i));
+                pw.print(",\"");
+                String str = getHistoryTagPoolString(i);
+                if (str != null) {
+                    str = str.replace("\\", "\\\\");
+                    str = str.replace("\"", "\\\"");
+                    pw.print(str);
+                }
+                pw.print("\"");
+                pw.println();
+            }
+        } else {
+            final long historyTotalSize = getHistoryTotalSize();
+            final long historyUsedSize = getHistoryUsedSize();
+            pw.print("Battery History (");
+            pw.print((100 * historyUsedSize) / historyTotalSize);
+            pw.print("% used, ");
+            printSizeValue(pw, historyUsedSize);
+            pw.print(" used of ");
+            printSizeValue(pw, historyTotalSize);
+            pw.print(", ");
+            pw.print(getHistoryStringPoolSize());
+            pw.print(" strings using ");
+            printSizeValue(pw, getHistoryStringPoolBytes());
+            pw.println("):");
+        }
+    }
+
+    private void dumpDailyLevelStepSummary(PrintWriter pw, String prefix, String label,
+            LevelStepTracker steps, StringBuilder tmpSb, int[] tmpOutInt) {
+        if (steps == null) {
+            return;
+        }
+        long timeRemaining = steps.computeTimeEstimate(0, 0, tmpOutInt);
+        if (timeRemaining >= 0) {
+            pw.print(prefix); pw.print(label); pw.print(" total time: ");
+            tmpSb.setLength(0);
+            formatTimeMs(tmpSb, timeRemaining);
+            pw.print(tmpSb);
+            pw.print(" (from "); pw.print(tmpOutInt[0]);
+            pw.println(" steps)");
+        }
+        for (int i=0; i< STEP_LEVEL_MODES_OF_INTEREST.length; i++) {
+            long estimatedTime = steps.computeTimeEstimate(STEP_LEVEL_MODES_OF_INTEREST[i],
+                    STEP_LEVEL_MODE_VALUES[i], tmpOutInt);
+            if (estimatedTime > 0) {
+                pw.print(prefix); pw.print(label); pw.print(" ");
+                pw.print(STEP_LEVEL_MODE_LABELS[i]);
+                pw.print(" time: ");
+                tmpSb.setLength(0);
+                formatTimeMs(tmpSb, estimatedTime);
+                pw.print(tmpSb);
+                pw.print(" (from "); pw.print(tmpOutInt[0]);
+                pw.println(" steps)");
+            }
+        }
+    }
+
+    private void dumpDailyPackageChanges(PrintWriter pw, String prefix,
+            ArrayList<PackageChange> changes) {
+        if (changes == null) {
+            return;
+        }
+        pw.print(prefix); pw.println("Package changes:");
+        for (int i=0; i<changes.size(); i++) {
+            PackageChange pc = changes.get(i);
+            if (pc.mUpdate) {
+                pw.print(prefix); pw.print("  Update "); pw.print(pc.mPackageName);
+                pw.print(" vers="); pw.println(pc.mVersionCode);
+            } else {
+                pw.print(prefix); pw.print("  Uninstall "); pw.println(pc.mPackageName);
+            }
+        }
+    }
+
+    /**
+     * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
+     *
+     * @param pw a Printer to receive the dump output.
+     */
+    @SuppressWarnings("unused")
+    public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+        synchronized (this) {
+            prepareForDumpLocked();
+        }
+
+        final boolean filtering = (flags
+                & (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
+
+        if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
+            dumpHistory(pw, flags, histStart, false);
+            pw.println();
+        }
+
+        if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
+            return;
+        }
+
+        synchronized (this) {
+            dumpLocked(context, pw, flags, reqUid, filtering);
+        }
+    }
+
+    private void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid,
+            boolean filtering) {
+        if (!filtering) {
+            SparseArray<? extends Uid> uidStats = getUidStats();
+            final int NU = uidStats.size();
+            boolean didPid = false;
+            long nowRealtime = SystemClock.elapsedRealtime();
+            for (int i=0; i<NU; i++) {
+                Uid uid = uidStats.valueAt(i);
+                SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
+                if (pids != null) {
+                    for (int j=0; j<pids.size(); j++) {
+                        Uid.Pid pid = pids.valueAt(j);
+                        if (!didPid) {
+                            pw.println("Per-PID Stats:");
+                            didPid = true;
+                        }
+                        long time = pid.mWakeSumMs + (pid.mWakeNesting > 0
+                                ? (nowRealtime - pid.mWakeStartMs) : 0);
+                        pw.print("  PID "); pw.print(pids.keyAt(j));
+                                pw.print(" wake time: ");
+                                TimeUtils.formatDuration(time, pw);
+                                pw.println("");
+                    }
+                }
+            }
+            if (didPid) {
+                pw.println();
+            }
+        }
+
+        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
+            if (dumpDurationSteps(pw, "  ", "Discharge step durations:",
+                    getDischargeLevelStepTracker(), false)) {
+                long timeRemaining = computeBatteryTimeRemaining(
+                    SystemClock.elapsedRealtime() * 1000);
+                if (timeRemaining >= 0) {
+                    pw.print("  Estimated discharge time remaining: ");
+                    TimeUtils.formatDuration(timeRemaining / 1000, pw);
+                    pw.println();
+                }
+                final LevelStepTracker steps = getDischargeLevelStepTracker();
+                for (int i=0; i< STEP_LEVEL_MODES_OF_INTEREST.length; i++) {
+                    dumpTimeEstimate(pw, "  Estimated ", STEP_LEVEL_MODE_LABELS[i], " time: ",
+                            steps.computeTimeEstimate(STEP_LEVEL_MODES_OF_INTEREST[i],
+                                    STEP_LEVEL_MODE_VALUES[i], null));
+                }
+                pw.println();
+            }
+            if (dumpDurationSteps(pw, "  ", "Charge step durations:",
+                    getChargeLevelStepTracker(), false)) {
+                long timeRemaining = computeChargeTimeRemaining(
+                    SystemClock.elapsedRealtime() * 1000);
+                if (timeRemaining >= 0) {
+                    pw.print("  Estimated charge time remaining: ");
+                    TimeUtils.formatDuration(timeRemaining / 1000, pw);
+                    pw.println();
+                }
+                pw.println();
+            }
+        }
+        if (!filtering || (flags & DUMP_DAILY_ONLY) != 0) {
+            pw.println("Daily stats:");
+            pw.print("  Current start time: ");
+            pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                    getCurrentDailyStartTime()).toString());
+            pw.print("  Next min deadline: ");
+            pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                    getNextMinDailyDeadline()).toString());
+            pw.print("  Next max deadline: ");
+            pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                    getNextMaxDailyDeadline()).toString());
+            StringBuilder sb = new StringBuilder(64);
+            int[] outInt = new int[1];
+            LevelStepTracker dsteps = getDailyDischargeLevelStepTracker();
+            LevelStepTracker csteps = getDailyChargeLevelStepTracker();
+            ArrayList<PackageChange> pkgc = getDailyPackageChanges();
+            if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0 || pkgc != null) {
+                if ((flags&DUMP_DAILY_ONLY) != 0 || !filtering) {
+                    if (dumpDurationSteps(pw, "    ", "  Current daily discharge step durations:",
+                            dsteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "      ", "Discharge", dsteps,
+                                sb, outInt);
+                    }
+                    if (dumpDurationSteps(pw, "    ", "  Current daily charge step durations:",
+                            csteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "      ", "Charge", csteps,
+                                sb, outInt);
+                    }
+                    dumpDailyPackageChanges(pw, "    ", pkgc);
+                } else {
+                    pw.println("  Current daily steps:");
+                    dumpDailyLevelStepSummary(pw, "    ", "Discharge", dsteps,
+                            sb, outInt);
+                    dumpDailyLevelStepSummary(pw, "    ", "Charge", csteps,
+                            sb, outInt);
+                }
+            }
+            DailyItem dit;
+            int curIndex = 0;
+            while ((dit=getDailyItemLocked(curIndex)) != null) {
+                curIndex++;
+                if ((flags&DUMP_DAILY_ONLY) != 0) {
+                    pw.println();
+                }
+                pw.print("  Daily from ");
+                pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", dit.mStartTime).toString());
+                pw.print(" to ");
+                pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", dit.mEndTime).toString());
+                pw.println(":");
+                if ((flags&DUMP_DAILY_ONLY) != 0 || !filtering) {
+                    if (dumpDurationSteps(pw, "      ",
+                            "    Discharge step durations:", dit.mDischargeSteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "        ", "Discharge", dit.mDischargeSteps,
+                                sb, outInt);
+                    }
+                    if (dumpDurationSteps(pw, "      ",
+                            "    Charge step durations:", dit.mChargeSteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "        ", "Charge", dit.mChargeSteps,
+                                sb, outInt);
+                    }
+                    dumpDailyPackageChanges(pw, "    ", dit.mPackageChanges);
+                } else {
+                    dumpDailyLevelStepSummary(pw, "    ", "Discharge", dit.mDischargeSteps,
+                            sb, outInt);
+                    dumpDailyLevelStepSummary(pw, "    ", "Charge", dit.mChargeSteps,
+                            sb, outInt);
+                }
+            }
+            pw.println();
+        }
+        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
+            pw.println("Statistics since last charge:");
+            pw.println("  System starts: " + getStartCount()
+                    + ", currently on battery: " + getIsOnBattery());
+            dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid,
+                    (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
+            pw.println();
+        }
+    }
+
+    // This is called from BatteryStatsService.
+    @SuppressWarnings("unused")
+    public void dumpCheckin(Context context, PrintWriter pw,
+            List<ApplicationInfo> apps, int flags, long histStart) {
+        synchronized (this) {
+            prepareForDumpLocked();
+
+            dumpLine(pw, 0 /* uid */, "i" /* category */, VERSION_DATA,
+                    CHECKIN_VERSION, getParcelVersion(), getStartPlatformVersion(),
+                    getEndPlatformVersion());
+        }
+
+        if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
+            dumpHistory(pw, flags, histStart, true);
+        }
+
+        if ((flags & DUMP_HISTORY_ONLY) != 0) {
+            return;
+        }
+
+        synchronized (this) {
+            dumpCheckinLocked(context, pw, apps, flags);
+        }
+    }
+
+    private void dumpCheckinLocked(Context context, PrintWriter pw, List<ApplicationInfo> apps,
+            int flags) {
+        if (apps != null) {
+            SparseArray<Pair<ArrayList<String>, MutableBoolean>> uids = new SparseArray<>();
+            for (int i=0; i<apps.size(); i++) {
+                ApplicationInfo ai = apps.get(i);
+                Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(
+                        UserHandle.getAppId(ai.uid));
+                if (pkgs == null) {
+                    pkgs = new Pair<>(new ArrayList<String>(), new MutableBoolean(false));
+                    uids.put(UserHandle.getAppId(ai.uid), pkgs);
+                }
+                pkgs.first.add(ai.packageName);
+            }
+            SparseArray<? extends Uid> uidStats = getUidStats();
+            final int NU = uidStats.size();
+            String[] lineArgs = new String[2];
+            for (int i=0; i<NU; i++) {
+                int uid = UserHandle.getAppId(uidStats.keyAt(i));
+                Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(uid);
+                if (pkgs != null && !pkgs.second.value) {
+                    pkgs.second.value = true;
+                    for (int j=0; j<pkgs.first.size(); j++) {
+                        lineArgs[0] = Integer.toString(uid);
+                        lineArgs[1] = pkgs.first.get(j);
+                        dumpLine(pw, 0 /* uid */, "i" /* category */, UID_DATA,
+                                (Object[])lineArgs);
+                    }
+                }
+            }
+        }
+        if ((flags & DUMP_DAILY_ONLY) == 0) {
+            dumpDurationSteps(pw, "", DISCHARGE_STEP_DATA, getDischargeLevelStepTracker(), true);
+            String[] lineArgs = new String[1];
+            long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime() * 1000);
+            if (timeRemaining >= 0) {
+                lineArgs[0] = Long.toString(timeRemaining);
+                dumpLine(pw, 0 /* uid */, "i" /* category */, DISCHARGE_TIME_REMAIN_DATA,
+                        (Object[])lineArgs);
+            }
+            dumpDurationSteps(pw, "", CHARGE_STEP_DATA, getChargeLevelStepTracker(), true);
+            timeRemaining = computeChargeTimeRemaining(SystemClock.elapsedRealtime() * 1000);
+            if (timeRemaining >= 0) {
+                lineArgs[0] = Long.toString(timeRemaining);
+                dumpLine(pw, 0 /* uid */, "i" /* category */, CHARGE_TIME_REMAIN_DATA,
+                        (Object[])lineArgs);
+            }
+            dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1,
+                    (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
+        }
+    }
+
+    /**
+     * Dump #STATS_SINCE_CHARGED batterystats data to a proto. If the flags include
+     * DUMP_INCLUDE_HISTORY or DUMP_HISTORY_ONLY, only the history will be dumped.
+     * @hide
+     */
+    public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
+            int flags, long histStart) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        prepareForDumpLocked();
+
+        if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
+            dumpProtoHistoryLocked(proto, flags, histStart);
+            proto.flush();
+            return;
+        }
+
+        final long bToken = proto.start(BatteryStatsServiceDumpProto.BATTERYSTATS);
+
+        proto.write(BatteryStatsProto.REPORT_VERSION, CHECKIN_VERSION);
+        proto.write(BatteryStatsProto.PARCEL_VERSION, getParcelVersion());
+        proto.write(BatteryStatsProto.START_PLATFORM_VERSION, getStartPlatformVersion());
+        proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion());
+
+        if ((flags & DUMP_DAILY_ONLY) == 0) {
+            final BatteryUsageStats stats = getBatteryUsageStats(context, false /* detailed */);
+            ProportionalAttributionCalculator proportionalAttributionCalculator =
+                    new ProportionalAttributionCalculator(context, stats);
+            dumpProtoAppsLocked(proto, stats, apps, proportionalAttributionCalculator);
+            dumpProtoSystemLocked(proto, stats);
+        }
+
+        proto.end(bToken);
+        proto.flush();
+    }
+
+    private void dumpProtoAppsLocked(ProtoOutputStream proto, BatteryUsageStats stats,
+            List<ApplicationInfo> apps,
+            ProportionalAttributionCalculator proportionalAttributionCalculator) {
+        final int which = STATS_SINCE_CHARGED;
+        final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
+        final long rawRealtimeMs = SystemClock.elapsedRealtime();
+        final long rawRealtimeUs = rawRealtimeMs * 1000;
+        final long batteryUptimeUs = getBatteryUptime(rawUptimeUs);
+
+        SparseArray<ArrayList<String>> aidToPackages = new SparseArray<>();
+        if (apps != null) {
+            for (int i = 0; i < apps.size(); ++i) {
+                ApplicationInfo ai = apps.get(i);
+                int aid = UserHandle.getAppId(ai.uid);
+                ArrayList<String> pkgs = aidToPackages.get(aid);
+                if (pkgs == null) {
+                    pkgs = new ArrayList<String>();
+                    aidToPackages.put(aid, pkgs);
+                }
+                pkgs.add(ai.packageName);
+            }
+        }
+
+        SparseArray<UidBatteryConsumer> uidToConsumer = new SparseArray<>();
+        final List<UidBatteryConsumer> consumers = stats.getUidBatteryConsumers();
+        for (int i = consumers.size() - 1; i >= 0; --i) {
+            final UidBatteryConsumer bs = consumers.get(i);
+            uidToConsumer.put(bs.getUid(), bs);
+        }
+
+        SparseArray<? extends Uid> uidStats = getUidStats();
+        final int n = uidStats.size();
+        for (int iu = 0; iu < n; ++iu) {
+            final long uTkn = proto.start(BatteryStatsProto.UIDS);
+            final Uid u = uidStats.valueAt(iu);
+
+            final int uid = uidStats.keyAt(iu);
+            proto.write(UidProto.UID, uid);
+
+            // Print packages and apk stats (UID_DATA & APK_DATA)
+            ArrayList<String> pkgs = aidToPackages.get(UserHandle.getAppId(uid));
+            if (pkgs == null) {
+                pkgs = new ArrayList<String>();
+            }
+            final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats =
+                    u.getPackageStats();
+            for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+                String pkg = packageStats.keyAt(ipkg);
+                final ArrayMap<String, ? extends  Uid.Pkg.Serv> serviceStats =
+                        packageStats.valueAt(ipkg).getServiceStats();
+                if (serviceStats.size() == 0) {
+                    // Due to the way ActivityManagerService logs wakeup alarms, some packages (for
+                    // example, "android") may be included in the packageStats that aren't part of
+                    // the UID. If they don't have any services, then they shouldn't be listed here.
+                    // These packages won't be a part in the pkgs List.
+                    continue;
+                }
+
+                final long pToken = proto.start(UidProto.PACKAGES);
+                proto.write(UidProto.Package.NAME, pkg);
+                // Remove from the packages list since we're logging it here.
+                pkgs.remove(pkg);
+
+                for (int isvc = serviceStats.size() - 1; isvc >= 0; --isvc) {
+                    final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+
+                    final long startTimeMs = roundUsToMs(ss.getStartTime(batteryUptimeUs, which));
+                    final int starts = ss.getStarts(which);
+                    final int launches = ss.getLaunches(which);
+                    if (startTimeMs == 0 && starts == 0 && launches == 0) {
+                        continue;
+                    }
+
+                    long sToken = proto.start(UidProto.Package.SERVICES);
+
+                    proto.write(UidProto.Package.Service.NAME, serviceStats.keyAt(isvc));
+                    proto.write(UidProto.Package.Service.START_DURATION_MS, startTimeMs);
+                    proto.write(UidProto.Package.Service.START_COUNT, starts);
+                    proto.write(UidProto.Package.Service.LAUNCH_COUNT, launches);
+
+                    proto.end(sToken);
+                }
+                proto.end(pToken);
+            }
+            // Print any remaining packages that weren't in the packageStats map. pkgs is pulled
+            // from PackageManager data. Packages are only included in packageStats if there was
+            // specific data tracked for them (services and wakeup alarms, etc.).
+            for (String p : pkgs) {
+                final long pToken = proto.start(UidProto.PACKAGES);
+                proto.write(UidProto.Package.NAME, p);
+                proto.end(pToken);
+            }
+
+            // Total wakelock data (AGGREGATED_WAKELOCK_DATA)
+            if (u.getAggregatedPartialWakelockTimer() != null) {
+                final Timer timer = u.getAggregatedPartialWakelockTimer();
+                // Times are since reset (regardless of 'which')
+                final long totTimeMs = timer.getTotalDurationMsLocked(rawRealtimeMs);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTimeMs = bgTimer != null
+                        ? bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0;
+                final long awToken = proto.start(UidProto.AGGREGATED_WAKELOCK);
+                proto.write(UidProto.AggregatedWakelock.PARTIAL_DURATION_MS, totTimeMs);
+                proto.write(UidProto.AggregatedWakelock.BACKGROUND_PARTIAL_DURATION_MS, bgTimeMs);
+                proto.end(awToken);
+            }
+
+            // Audio (AUDIO_DATA)
+            dumpTimer(proto, UidProto.AUDIO, u.getAudioTurnedOnTimer(), rawRealtimeUs, which);
+
+            // Bluetooth Controller (BLUETOOTH_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.BLUETOOTH_CONTROLLER,
+                    u.getBluetoothControllerActivity(), which);
+
+            // BLE scans (BLUETOOTH_MISC_DATA) (uses totalDurationMsLocked and MaxDurationMsLocked)
+            final Timer bleTimer = u.getBluetoothScanTimer();
+            if (bleTimer != null) {
+                final long bmToken = proto.start(UidProto.BLUETOOTH_MISC);
+
+                dumpTimer(proto, UidProto.BluetoothMisc.APPORTIONED_BLE_SCAN, bleTimer,
+                        rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN,
+                        u.getBluetoothScanBackgroundTimer(), rawRealtimeUs, which);
+                // Unoptimized scan timer. Unpooled and since reset (regardless of 'which').
+                dumpTimer(proto, UidProto.BluetoothMisc.UNOPTIMIZED_BLE_SCAN,
+                        u.getBluetoothUnoptimizedScanTimer(), rawRealtimeUs, which);
+                // Unoptimized bg scan timer. Unpooled and since reset (regardless of 'which').
+                dumpTimer(proto, UidProto.BluetoothMisc.BACKGROUND_UNOPTIMIZED_BLE_SCAN,
+                        u.getBluetoothUnoptimizedScanBackgroundTimer(), rawRealtimeUs, which);
+                // Result counters
+                proto.write(UidProto.BluetoothMisc.BLE_SCAN_RESULT_COUNT,
+                        u.getBluetoothScanResultCounter() != null
+                            ? u.getBluetoothScanResultCounter().getCountLocked(which) : 0);
+                proto.write(UidProto.BluetoothMisc.BACKGROUND_BLE_SCAN_RESULT_COUNT,
+                        u.getBluetoothScanResultBgCounter() != null
+                            ? u.getBluetoothScanResultBgCounter().getCountLocked(which) : 0);
+
+                proto.end(bmToken);
+            }
+
+            // Camera (CAMERA_DATA)
+            dumpTimer(proto, UidProto.CAMERA, u.getCameraTurnedOnTimer(), rawRealtimeUs, which);
+
+            // CPU stats (CPU_DATA & CPU_TIMES_AT_FREQ_DATA)
+            final long cpuToken = proto.start(UidProto.CPU);
+            proto.write(UidProto.Cpu.USER_DURATION_MS, roundUsToMs(u.getUserCpuTimeUs(which)));
+            proto.write(UidProto.Cpu.SYSTEM_DURATION_MS, roundUsToMs(u.getSystemCpuTimeUs(which)));
+
+            final long[] cpuFreqs = getCpuFreqs();
+            if (cpuFreqs != null) {
+                final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
+                // If total cpuFreqTimes is null, then we don't need to check for
+                // screenOffCpuFreqTimes.
+                if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
+                    long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
+                    if (screenOffCpuFreqTimeMs == null) {
+                        screenOffCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
+                    }
+                    for (int ic = 0; ic < cpuFreqTimeMs.length; ++ic) {
+                        long cToken = proto.start(UidProto.Cpu.BY_FREQUENCY);
+                        proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1);
+                        proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS,
+                                cpuFreqTimeMs[ic]);
+                        proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS,
+                                screenOffCpuFreqTimeMs[ic]);
+                        proto.end(cToken);
+                    }
+                }
+            }
+
+            final long[] timesInFreqMs = new long[getCpuFreqCount()];
+            final long[] timesInFreqScreenOffMs = new long[getCpuFreqCount()];
+            for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
+                if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
+                    if (!u.getScreenOffCpuFreqTimes(timesInFreqScreenOffMs, procState)) {
+                        Arrays.fill(timesInFreqScreenOffMs, 0);
+                    }
+                    final long procToken = proto.start(UidProto.Cpu.BY_PROCESS_STATE);
+                    proto.write(UidProto.Cpu.ByProcessState.PROCESS_STATE, procState);
+                    for (int ic = 0; ic < timesInFreqMs.length; ++ic) {
+                        long cToken = proto.start(UidProto.Cpu.ByProcessState.BY_FREQUENCY);
+                        proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1);
+                        proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS,
+                                timesInFreqMs[ic]);
+                        proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS,
+                                timesInFreqScreenOffMs[ic]);
+                        proto.end(cToken);
+                    }
+                    proto.end(procToken);
+                }
+            }
+            proto.end(cpuToken);
+
+            // Flashlight (FLASHLIGHT_DATA)
+            dumpTimer(proto, UidProto.FLASHLIGHT, u.getFlashlightTurnedOnTimer(),
+                    rawRealtimeUs, which);
+
+            // Foreground activity (FOREGROUND_ACTIVITY_DATA)
+            dumpTimer(proto, UidProto.FOREGROUND_ACTIVITY, u.getForegroundActivityTimer(),
+                    rawRealtimeUs, which);
+
+            // Foreground service (FOREGROUND_SERVICE_DATA)
+            dumpTimer(proto, UidProto.FOREGROUND_SERVICE, u.getForegroundServiceTimer(),
+                    rawRealtimeUs, which);
+
+            // Job completion (JOB_COMPLETION_DATA)
+            final ArrayMap<String, SparseIntArray> completions = u.getJobCompletionStats();
+            for (int ic = 0; ic < completions.size(); ++ic) {
+                SparseIntArray types = completions.valueAt(ic);
+                if (types != null) {
+                    final long jcToken = proto.start(UidProto.JOB_COMPLETION);
+
+                    proto.write(UidProto.JobCompletion.NAME, completions.keyAt(ic));
+
+                    for (int r : JobParameters.getJobStopReasonCodes()) {
+                        long rToken = proto.start(UidProto.JobCompletion.REASON_COUNT);
+                        proto.write(UidProto.JobCompletion.ReasonCount.NAME, r);
+                        proto.write(UidProto.JobCompletion.ReasonCount.COUNT, types.get(r, 0));
+                        proto.end(rToken);
+                    }
+
+                    proto.end(jcToken);
+                }
+            }
+
+            // Scheduled jobs (JOB_DATA)
+            final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+            for (int ij = jobs.size() - 1; ij >= 0; --ij) {
+                final Timer timer = jobs.valueAt(ij);
+                final Timer bgTimer = timer.getSubTimer();
+                final long jToken = proto.start(UidProto.JOBS);
+
+                proto.write(UidProto.Job.NAME, jobs.keyAt(ij));
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Job.TOTAL, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Job.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(jToken);
+            }
+
+            // Modem Controller (MODEM_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.MODEM_CONTROLLER,
+                    u.getModemControllerActivity(), which);
+
+            // Network stats (NETWORK_DATA)
+            final long nToken = proto.start(UidProto.NETWORK);
+            proto.write(UidProto.Network.MOBILE_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+            proto.write(UidProto.Network.BT_BYTES_RX,
+                    u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
+            proto.write(UidProto.Network.BT_BYTES_TX,
+                    u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_RX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_TX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_RX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_TX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_ACTIVE_DURATION_MS,
+                    roundUsToMs(u.getMobileRadioActiveTime(which)));
+            proto.write(UidProto.Network.MOBILE_ACTIVE_COUNT,
+                    u.getMobileRadioActiveCount(which));
+            proto.write(UidProto.Network.MOBILE_WAKEUP_COUNT,
+                    u.getMobileRadioApWakeupCount(which));
+            proto.write(UidProto.Network.WIFI_WAKEUP_COUNT,
+                    u.getWifiRadioApWakeupCount(which));
+            proto.write(UidProto.Network.MOBILE_BYTES_BG_RX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_BG_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_BYTES_BG_TX,
+                    u.getNetworkActivityBytes(NETWORK_MOBILE_BG_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_BG_RX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_BG_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_BYTES_BG_TX,
+                    u.getNetworkActivityBytes(NETWORK_WIFI_BG_TX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_BG_RX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_BG_RX_DATA, which));
+            proto.write(UidProto.Network.MOBILE_PACKETS_BG_TX,
+                    u.getNetworkActivityPackets(NETWORK_MOBILE_BG_TX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_BG_RX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_BG_RX_DATA, which));
+            proto.write(UidProto.Network.WIFI_PACKETS_BG_TX,
+                    u.getNetworkActivityPackets(NETWORK_WIFI_BG_TX_DATA, which));
+            proto.end(nToken);
+
+            // Power use item (POWER_USE_ITEM_DATA)
+            UidBatteryConsumer consumer = uidToConsumer.get(uid);
+            if (consumer != null) {
+                final long bsToken = proto.start(UidProto.POWER_USE_ITEM);
+                proto.write(UidProto.PowerUseItem.COMPUTED_POWER_MAH, consumer.getConsumedPower());
+                proto.write(UidProto.PowerUseItem.SHOULD_HIDE,
+                        proportionalAttributionCalculator.isSystemBatteryConsumer(consumer));
+                proto.write(UidProto.PowerUseItem.SCREEN_POWER_MAH,
+                        consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN));
+                proto.write(UidProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
+                        proportionalAttributionCalculator.getProportionalPowerMah(consumer));
+                proto.end(bsToken);
+            }
+
+            // Processes (PROCESS_DATA)
+            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats =
+                    u.getProcessStats();
+            for (int ipr = processStats.size() - 1; ipr >= 0; --ipr) {
+                final Uid.Proc ps = processStats.valueAt(ipr);
+                final long prToken = proto.start(UidProto.PROCESS);
+
+                proto.write(UidProto.Process.NAME, processStats.keyAt(ipr));
+                proto.write(UidProto.Process.USER_DURATION_MS, ps.getUserTime(which));
+                proto.write(UidProto.Process.SYSTEM_DURATION_MS, ps.getSystemTime(which));
+                proto.write(UidProto.Process.FOREGROUND_DURATION_MS, ps.getForegroundTime(which));
+                proto.write(UidProto.Process.START_COUNT, ps.getStarts(which));
+                proto.write(UidProto.Process.ANR_COUNT, ps.getNumAnrs(which));
+                proto.write(UidProto.Process.CRASH_COUNT, ps.getNumCrashes(which));
+
+                proto.end(prToken);
+            }
+
+            // Sensors (SENSOR_DATA)
+            final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            for (int ise = 0; ise < sensors.size(); ++ise) {
+                final Uid.Sensor se = sensors.valueAt(ise);
+                final Timer timer = se.getSensorTime();
+                if (timer == null) {
+                    continue;
+                }
+                final Timer bgTimer = se.getSensorBackgroundTime();
+                final int sensorNumber = sensors.keyAt(ise);
+                final long seToken = proto.start(UidProto.SENSORS);
+
+                proto.write(UidProto.Sensor.ID, sensorNumber);
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Sensor.APPORTIONED, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Sensor.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(seToken);
+            }
+
+            // State times (STATE_TIME_DATA)
+            for (int ips = 0; ips < Uid.NUM_PROCESS_STATE; ++ips) {
+                long durMs = roundUsToMs(u.getProcessStateTime(ips, rawRealtimeUs, which));
+                if (durMs == 0) {
+                    continue;
+                }
+                final long stToken = proto.start(UidProto.STATES);
+                proto.write(UidProto.StateTime.STATE, ips);
+                proto.write(UidProto.StateTime.DURATION_MS, durMs);
+                proto.end(stToken);
+            }
+
+            // Syncs (SYNC_DATA)
+            final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+            for (int isy = syncs.size() - 1; isy >= 0; --isy) {
+                final Timer timer = syncs.valueAt(isy);
+                final Timer bgTimer = timer.getSubTimer();
+                final long syToken = proto.start(UidProto.SYNCS);
+
+                proto.write(UidProto.Sync.NAME, syncs.keyAt(isy));
+                // Background uses totalDurationMsLocked, while total uses totalTimeLocked
+                dumpTimer(proto, UidProto.Sync.TOTAL, timer, rawRealtimeUs, which);
+                dumpTimer(proto, UidProto.Sync.BACKGROUND, bgTimer, rawRealtimeUs, which);
+
+                proto.end(syToken);
+            }
+
+            // User activity (USER_ACTIVITY_DATA)
+            if (u.hasUserActivity()) {
+                for (int i = 0; i < Uid.NUM_USER_ACTIVITY_TYPES; ++i) {
+                    int val = u.getUserActivityCount(i, which);
+                    if (val != 0) {
+                        final long uaToken = proto.start(UidProto.USER_ACTIVITY);
+                        proto.write(UidProto.UserActivity.NAME, i);
+                        proto.write(UidProto.UserActivity.COUNT, val);
+                        proto.end(uaToken);
+                    }
+                }
+            }
+
+            // Vibrator (VIBRATOR_DATA)
+            dumpTimer(proto, UidProto.VIBRATOR, u.getVibratorOnTimer(), rawRealtimeUs, which);
+
+            // Video (VIDEO_DATA)
+            dumpTimer(proto, UidProto.VIDEO, u.getVideoTurnedOnTimer(), rawRealtimeUs, which);
+
+            // Wakelocks (WAKELOCK_DATA)
+            final ArrayMap<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
+            for (int iw = wakelocks.size() - 1; iw >= 0; --iw) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+                final long wToken = proto.start(UidProto.WAKELOCKS);
+                proto.write(UidProto.Wakelock.NAME, wakelocks.keyAt(iw));
+                dumpTimer(proto, UidProto.Wakelock.FULL, wl.getWakeTime(WAKE_TYPE_FULL),
+                        rawRealtimeUs, which);
+                final Timer pTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (pTimer != null) {
+                    dumpTimer(proto, UidProto.Wakelock.PARTIAL, pTimer, rawRealtimeUs, which);
+                    dumpTimer(proto, UidProto.Wakelock.BACKGROUND_PARTIAL, pTimer.getSubTimer(),
+                            rawRealtimeUs, which);
+                }
+                dumpTimer(proto, UidProto.Wakelock.WINDOW, wl.getWakeTime(WAKE_TYPE_WINDOW),
+                        rawRealtimeUs, which);
+                proto.end(wToken);
+            }
+
+            // Wifi Multicast Wakelock (WIFI_MULTICAST_WAKELOCK_DATA)
+            dumpTimer(proto, UidProto.WIFI_MULTICAST_WAKELOCK, u.getMulticastWakelockStats(),
+                    rawRealtimeUs, which);
+
+            // Wakeup alarms (WAKEUP_ALARM_DATA)
+            for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
+                final Uid.Pkg ps = packageStats.valueAt(ipkg);
+                final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+                for (int iwa = alarms.size() - 1; iwa >= 0; --iwa) {
+                    final long waToken = proto.start(UidProto.WAKEUP_ALARM);
+                    proto.write(UidProto.WakeupAlarm.NAME, alarms.keyAt(iwa));
+                    proto.write(UidProto.WakeupAlarm.COUNT,
+                            alarms.valueAt(iwa).getCountLocked(which));
+                    proto.end(waToken);
+                }
+            }
+
+            // Wifi Controller (WIFI_CONTROLLER_DATA)
+            dumpControllerActivityProto(proto, UidProto.WIFI_CONTROLLER,
+                    u.getWifiControllerActivity(), which);
+
+            // Wifi data (WIFI_DATA)
+            final long wToken = proto.start(UidProto.WIFI);
+            proto.write(UidProto.Wifi.FULL_WIFI_LOCK_DURATION_MS,
+                    roundUsToMs(u.getFullWifiLockTime(rawRealtimeUs, which)));
+            dumpTimer(proto, UidProto.Wifi.APPORTIONED_SCAN, u.getWifiScanTimer(),
+                    rawRealtimeUs, which);
+            proto.write(UidProto.Wifi.RUNNING_DURATION_MS,
+                    roundUsToMs(u.getWifiRunningTime(rawRealtimeUs, which)));
+            dumpTimer(proto, UidProto.Wifi.BACKGROUND_SCAN, u.getWifiScanBackgroundTimer(),
+                    rawRealtimeUs, which);
+            proto.end(wToken);
+
+            proto.end(uTkn);
+        }
+    }
+
+    private void dumpProtoHistoryLocked(ProtoOutputStream proto, int flags, long histStart) {
+        proto.write(BatteryStatsServiceDumpHistoryProto.REPORT_VERSION, CHECKIN_VERSION);
+        proto.write(BatteryStatsServiceDumpHistoryProto.PARCEL_VERSION, getParcelVersion());
+        proto.write(BatteryStatsServiceDumpHistoryProto.START_PLATFORM_VERSION,
+                getStartPlatformVersion());
+        proto.write(BatteryStatsServiceDumpHistoryProto.END_PLATFORM_VERSION,
+                getEndPlatformVersion());
+            long token;
+            // History string pool (HISTORY_STRING_POOL)
+            for (int i = 0; i < getHistoryStringPoolSize(); ++i) {
+                token = proto.start(BatteryStatsServiceDumpHistoryProto.KEYS);
+                proto.write(BatteryStatsServiceDumpHistoryProto.Key.INDEX, i);
+                proto.write(BatteryStatsServiceDumpHistoryProto.Key.UID, getHistoryTagPoolUid(i));
+                proto.write(BatteryStatsServiceDumpHistoryProto.Key.TAG,
+                        getHistoryTagPoolString(i));
+                proto.end(token);
+            }
+
+        // History data (HISTORY_DATA)
+        final HistoryPrinter hprinter = new HistoryPrinter();
+        long lastTime = -1;
+        long baseTime = -1;
+        boolean printed = false;
+        HistoryEventTracker tracker = null;
+        try (BatteryStatsHistoryIterator iterator = iterateBatteryStatsHistory()) {
+            HistoryItem rec;
+            while ((rec = iterator.next()) != null) {
+                lastTime = rec.time;
+                if (baseTime < 0) {
+                    baseTime = lastTime;
+                }
+                if (rec.time >= histStart) {
+                    if (histStart >= 0 && !printed) {
+                        if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                                || rec.cmd == HistoryItem.CMD_RESET
+                                || rec.cmd == HistoryItem.CMD_START
+                                || rec.cmd == HistoryItem.CMD_SHUTDOWN) {
+                            printed = true;
+                            hprinter.printNextItem(proto, rec, baseTime,
+                                    (flags & DUMP_VERBOSE) != 0);
+                            rec.cmd = HistoryItem.CMD_UPDATE;
+                        } else if (rec.currentTime != 0) {
+                            printed = true;
+                            byte cmd = rec.cmd;
+                            rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+                            hprinter.printNextItem(proto, rec, baseTime,
+                                    (flags & DUMP_VERBOSE) != 0);
+                            rec.cmd = cmd;
+                        }
+                        if (tracker != null) {
+                            if (rec.cmd != HistoryItem.CMD_UPDATE) {
+                                hprinter.printNextItem(proto, rec, baseTime,
+                                        (flags & DUMP_VERBOSE) != 0);
+                                rec.cmd = HistoryItem.CMD_UPDATE;
+                            }
+                            int oldEventCode = rec.eventCode;
+                            HistoryTag oldEventTag = rec.eventTag;
+                            rec.eventTag = new HistoryTag();
+                            for (int i = 0; i < HistoryItem.EVENT_COUNT; i++) {
+                                HashMap<String, SparseIntArray> active =
+                                        tracker.getStateForEvent(i);
+                                if (active == null) {
+                                    continue;
+                                }
+                                for (HashMap.Entry<String, SparseIntArray> ent
+                                        : active.entrySet()) {
+                                    SparseIntArray uids = ent.getValue();
+                                    for (int j = 0; j < uids.size(); j++) {
+                                        rec.eventCode = i;
+                                        rec.eventTag.string = ent.getKey();
+                                        rec.eventTag.uid = uids.keyAt(j);
+                                        rec.eventTag.poolIdx = uids.valueAt(j);
+                                        hprinter.printNextItem(proto, rec, baseTime,
+                                                (flags & DUMP_VERBOSE) != 0);
+                                        rec.wakeReasonTag = null;
+                                        rec.wakelockTag = null;
+                                    }
+                                }
+                            }
+                            rec.eventCode = oldEventCode;
+                            rec.eventTag = oldEventTag;
+                            tracker = null;
+                        }
+                    }
+                    hprinter.printNextItem(proto, rec, baseTime,
+                            (flags & DUMP_VERBOSE) != 0);
+                }
+            }
+            if (histStart >= 0) {
+                commitCurrentHistoryBatchLocked();
+                proto.write(BatteryStatsServiceDumpHistoryProto.CSV_LINES,
+                        "NEXT: " + (lastTime + 1));
+            }
+        }
+    }
+
+    private void dumpProtoSystemLocked(ProtoOutputStream proto, BatteryUsageStats stats) {
+        final long sToken = proto.start(BatteryStatsProto.SYSTEM);
+        final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
+        final long rawRealtimeMs = SystemClock.elapsedRealtime();
+        final long rawRealtimeUs = rawRealtimeMs * 1000;
+        final int which = STATS_SINCE_CHARGED;
+
+        // Battery data (BATTERY_DATA)
+        final long bToken = proto.start(SystemProto.BATTERY);
+        proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime());
+        proto.write(SystemProto.Battery.START_COUNT, getStartCount());
+        proto.write(SystemProto.Battery.TOTAL_REALTIME_MS,
+                computeRealtime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.TOTAL_UPTIME_MS,
+                computeUptime(rawUptimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.BATTERY_REALTIME_MS,
+                computeBatteryRealtime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.BATTERY_UPTIME_MS,
+                computeBatteryUptime(rawUptimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.SCREEN_OFF_REALTIME_MS,
+                computeBatteryScreenOffRealtime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.SCREEN_OFF_UPTIME_MS,
+                computeBatteryScreenOffUptime(rawUptimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.SCREEN_DOZE_DURATION_MS,
+                getScreenDozeTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Battery.ESTIMATED_BATTERY_CAPACITY_MAH,
+                getEstimatedBatteryCapacity());
+        proto.write(SystemProto.Battery.MIN_LEARNED_BATTERY_CAPACITY_UAH,
+                getMinLearnedBatteryCapacity());
+        proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH,
+                getMaxLearnedBatteryCapacity());
+        proto.end(bToken);
+
+        // Battery discharge (BATTERY_DISCHARGE_DATA)
+        final long bdToken = proto.start(SystemProto.BATTERY_DISCHARGE);
+        proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE,
+                getLowDischargeAmountSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE,
+                getHighDischargeAmountSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.SCREEN_ON_SINCE_CHARGE,
+                getDischargeAmountScreenOnSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.SCREEN_OFF_SINCE_CHARGE,
+                getDischargeAmountScreenOffSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.SCREEN_DOZE_SINCE_CHARGE,
+                getDischargeAmountScreenDozeSinceCharge());
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH,
+                getUahDischarge(which) / 1000);
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_OFF,
+                getUahDischargeScreenOff(which) / 1000);
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE,
+                getUahDischargeScreenDoze(which) / 1000);
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_LIGHT_DOZE,
+                getUahDischargeLightDoze(which) / 1000);
+        proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_DEEP_DOZE,
+                getUahDischargeDeepDoze(which) / 1000);
+        proto.end(bdToken);
+
+        // Time remaining
+        long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs);
+        // These are part of a oneof, so we should only set one of them.
+        if (timeRemainingUs >= 0) {
+            // Charge time remaining (CHARGE_TIME_REMAIN_DATA)
+            proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
+        } else {
+            timeRemainingUs = computeBatteryTimeRemaining(rawRealtimeUs);
+            // Discharge time remaining (DISCHARGE_TIME_REMAIN_DATA)
+            if (timeRemainingUs >= 0) {
+                proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
+            } else {
+                proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, -1);
+            }
+        }
+
+        // Charge step (CHARGE_STEP_DATA)
+        dumpDurationSteps(proto, SystemProto.CHARGE_STEP, getChargeLevelStepTracker());
+
+        // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
+        for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
+            // Map OTHER to TelephonyManager.NETWORK_TYPE_UNKNOWN and mark NONE as a boolean.
+            boolean isNone = (i == DATA_CONNECTION_OUT_OF_SERVICE);
+            int telephonyNetworkType = i;
+            if (i == DATA_CONNECTION_OTHER || i == DATA_CONNECTION_EMERGENCY_SERVICE) {
+                telephonyNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            }
+            final long pdcToken = proto.start(SystemProto.DATA_CONNECTION);
+            if (isNone) {
+                proto.write(SystemProto.DataConnection.IS_NONE, isNone);
+            } else {
+                proto.write(SystemProto.DataConnection.NAME, telephonyNetworkType);
+            }
+            dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(pdcToken);
+        }
+
+        // Discharge step (DISCHARGE_STEP_DATA)
+        dumpDurationSteps(proto, SystemProto.DISCHARGE_STEP, getDischargeLevelStepTracker());
+
+        // CPU frequencies (GLOBAL_CPU_FREQ_DATA)
+        final long[] cpuFreqs = getCpuFreqs();
+        if (cpuFreqs != null) {
+            for (long i : cpuFreqs) {
+                proto.write(SystemProto.CPU_FREQUENCY, i);
+            }
+        }
+
+        // Bluetooth controller (GLOBAL_BLUETOOTH_CONTROLLER_DATA)
+        dumpControllerActivityProto(proto, SystemProto.GLOBAL_BLUETOOTH_CONTROLLER,
+                getBluetoothControllerActivity(), which);
+
+        // Modem controller (GLOBAL_MODEM_CONTROLLER_DATA)
+        dumpControllerActivityProto(proto, SystemProto.GLOBAL_MODEM_CONTROLLER,
+                getModemControllerActivity(), which);
+
+        // Global network data (GLOBAL_NETWORK_DATA)
+        final long gnToken = proto.start(SystemProto.GLOBAL_NETWORK);
+        proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX,
+                getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX,
+                getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_RX,
+                getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_TX,
+                getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_RX,
+                getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_TX,
+                getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_RX,
+                getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_TX,
+                getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.BT_BYTES_RX,
+                getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
+        proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX,
+                getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
+        proto.end(gnToken);
+
+        // Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA)
+        dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER,
+                getWifiControllerActivity(), which);
+
+
+        // Global wifi (GLOBAL_WIFI_DATA)
+        final long gwToken = proto.start(SystemProto.GLOBAL_WIFI);
+        proto.write(SystemProto.GlobalWifi.ON_DURATION_MS,
+                getWifiOnTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS,
+                getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000);
+        proto.end(gwToken);
+
+        // Kernel wakelock (KERNEL_WAKELOCK_DATA)
+        final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
+        for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
+            final long kwToken = proto.start(SystemProto.KERNEL_WAKELOCK);
+            proto.write(SystemProto.KernelWakelock.NAME, ent.getKey());
+            dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(),
+                    rawRealtimeUs, which);
+            proto.end(kwToken);
+        }
+
+        // Misc (MISC_DATA)
+        // Calculate wakelock times across all uids.
+        long fullWakeLockTimeTotalUs = 0;
+        long partialWakeLockTimeTotalUs = 0;
+
+        final SparseArray<? extends Uid> uidStats = getUidStats();
+        for (int iu = 0; iu < uidStats.size(); iu++) {
+            final Uid u = uidStats.valueAt(iu);
+
+            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks =
+                    u.getWakelockStats();
+            for (int iw = wakelocks.size() - 1; iw >= 0; --iw) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+
+                final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+                if (fullWakeTimer != null) {
+                    fullWakeLockTimeTotalUs += fullWakeTimer.getTotalTimeLocked(rawRealtimeUs,
+                            which);
+                }
+
+                final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (partialWakeTimer != null) {
+                    partialWakeLockTimeTotalUs += partialWakeTimer.getTotalTimeLocked(
+                        rawRealtimeUs, which);
+                }
+            }
+        }
+        final long mToken = proto.start(SystemProto.MISC);
+        proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS,
+                getScreenOnTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS,
+                getPhoneOnTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.FULL_WAKELOCK_TOTAL_DURATION_MS,
+                fullWakeLockTimeTotalUs / 1000);
+        proto.write(SystemProto.Misc.PARTIAL_WAKELOCK_TOTAL_DURATION_MS,
+                partialWakeLockTimeTotalUs / 1000);
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_DURATION_MS,
+                getMobileRadioActiveTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_ADJUSTED_TIME_MS,
+                getMobileRadioActiveAdjustedTime(which) / 1000);
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_COUNT,
+                getMobileRadioActiveCount(which));
+        proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_UNKNOWN_DURATION_MS,
+                getMobileRadioActiveUnknownTime(which) / 1000);
+        proto.write(SystemProto.Misc.INTERACTIVE_DURATION_MS,
+                getInteractiveTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.BATTERY_SAVER_MODE_ENABLED_DURATION_MS,
+                getPowerSaveModeEnabledTime(rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.NUM_CONNECTIVITY_CHANGES,
+                getNumConnectivityChange(which));
+        proto.write(SystemProto.Misc.DEEP_DOZE_ENABLED_DURATION_MS,
+                getDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.DEEP_DOZE_COUNT,
+                getDeviceIdleModeCount(DEVICE_IDLE_MODE_DEEP, which));
+        proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_DURATION_MS,
+                getDeviceIdlingTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_COUNT,
+                getDeviceIdlingCount(DEVICE_IDLE_MODE_DEEP, which));
+        proto.write(SystemProto.Misc.LONGEST_DEEP_DOZE_DURATION_MS,
+                getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP));
+        proto.write(SystemProto.Misc.LIGHT_DOZE_ENABLED_DURATION_MS,
+                getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.LIGHT_DOZE_COUNT,
+                getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which));
+        proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_DURATION_MS,
+                getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
+        proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_COUNT,
+                getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
+        proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS,
+                getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
+        proto.end(mToken);
+
+        // Wifi multicast wakelock total stats (WIFI_MULTICAST_WAKELOCK_TOTAL_DATA)
+        final long multicastWakeLockTimeTotalUs =
+                getWifiMulticastWakelockTime(rawRealtimeUs, which);
+        final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which);
+        final long wmctToken = proto.start(SystemProto.WIFI_MULTICAST_WAKELOCK_TOTAL);
+        proto.write(SystemProto.WifiMulticastWakelockTotal.DURATION_MS,
+                multicastWakeLockTimeTotalUs / 1000);
+        proto.write(SystemProto.WifiMulticastWakelockTotal.COUNT,
+                multicastWakeLockCountTotal);
+        proto.end(wmctToken);
+
+        final BatteryConsumer deviceConsumer = stats.getAggregateBatteryConsumer(
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+
+        for (int powerComponent = 0; powerComponent < BatteryConsumer.POWER_COMPONENT_COUNT;
+                powerComponent++) {
+            int n = SystemProto.PowerUseItem.UNKNOWN_SIPPER;
+            switch (powerComponent) {
+                case BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY:
+                    n = SystemProto.PowerUseItem.AMBIENT_DISPLAY;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_IDLE:
+                    n = SystemProto.PowerUseItem.IDLE;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO:
+                    n = SystemProto.PowerUseItem.CELL;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_PHONE:
+                    n = SystemProto.PowerUseItem.PHONE;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_WIFI:
+                    n = SystemProto.PowerUseItem.WIFI;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_BLUETOOTH:
+                    n = SystemProto.PowerUseItem.BLUETOOTH;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_SCREEN:
+                    n = SystemProto.PowerUseItem.SCREEN;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_FLASHLIGHT:
+                    n = SystemProto.PowerUseItem.FLASHLIGHT;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_CAMERA:
+                    n = SystemProto.PowerUseItem.CAMERA;
+                    break;
+                case BatteryConsumer.POWER_COMPONENT_MEMORY:
+                    n = SystemProto.PowerUseItem.MEMORY;
+                    break;
+            }
+            final long puiToken = proto.start(SystemProto.POWER_USE_ITEM);
+            proto.write(SystemProto.PowerUseItem.NAME, n);
+            proto.write(SystemProto.PowerUseItem.UID, 0);
+            proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH,
+                    deviceConsumer.getConsumedPower(powerComponent));
+            proto.write(SystemProto.PowerUseItem.SHOULD_HIDE,
+                    shouldHidePowerComponent(powerComponent));
+            proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, 0);
+            proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH, 0);
+            proto.end(puiToken);
+        }
+
+        // Power use summary (POWER_USE_SUMMARY_DATA)
+        final long pusToken = proto.start(SystemProto.POWER_USE_SUMMARY);
+        proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH,
+                stats.getBatteryCapacity());
+        proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, stats.getConsumedPower());
+        proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH,
+                stats.getDischargedPowerRange().getLower());
+        proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH,
+                stats.getDischargedPowerRange().getUpper());
+        proto.end(pusToken);
+
+        // RPM stats (RESOURCE_POWER_MANAGER_DATA)
+        final Map<String, ? extends Timer> rpmStats = getRpmStats();
+        final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
+        for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
+            final long rpmToken = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
+            proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey());
+            dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL,
+                    ent.getValue(), rawRealtimeUs, which);
+            dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF,
+                    screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which);
+            proto.end(rpmToken);
+        }
+
+        // Screen brightness (SCREEN_BRIGHTNESS_DATA)
+        for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) {
+            final long sbToken = proto.start(SystemProto.SCREEN_BRIGHTNESS);
+            proto.write(SystemProto.ScreenBrightness.NAME, i);
+            dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(sbToken);
+        }
+
+        // Signal scanning time (SIGNAL_SCANNING_TIME_DATA)
+        dumpTimer(proto, SystemProto.SIGNAL_SCANNING, getPhoneSignalScanningTimer(), rawRealtimeUs,
+                which);
+
+        // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
+        for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); ++i) {
+            final long pssToken = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
+            proto.write(SystemProto.PhoneSignalStrength.NAME, i);
+            dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(pssToken);
+        }
+
+        // Wakeup reasons (WAKEUP_REASON_DATA)
+        final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+        for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+            final long wrToken = proto.start(SystemProto.WAKEUP_REASON);
+            proto.write(SystemProto.WakeupReason.NAME, ent.getKey());
+            dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which);
+            proto.end(wrToken);
+        }
+
+        // Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA)
+        for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) {
+            final long wssToken = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
+            proto.write(SystemProto.WifiSignalStrength.NAME, i);
+            dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(wssToken);
+        }
+
+        // Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA)
+        for (int i = 0; i < NUM_WIFI_STATES; ++i) {
+            final long wsToken = proto.start(SystemProto.WIFI_STATE);
+            proto.write(SystemProto.WifiState.NAME, i);
+            dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(wsToken);
+        }
+
+        // Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA)
+        for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) {
+            final long wssToken = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
+            proto.write(SystemProto.WifiSupplicantState.NAME, i);
+            dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i),
+                    rawRealtimeUs, which);
+            proto.end(wssToken);
+        }
+
+        proto.end(sToken);
+    }
+
+    /**
+     * Returns true if the device does not have data-capable telephony.
+     */
+    public static boolean checkWifiOnly(Context context) {
+        final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+        if (tm == null) {
+            return false;
+        }
+        return !tm.isDataCapable();
+    }
+
+    protected abstract BatteryUsageStats getBatteryUsageStats(Context context, boolean detailed);
+
+    private boolean shouldHidePowerComponent(int powerComponent) {
+        return powerComponent == BatteryConsumer.POWER_COMPONENT_IDLE
+                || powerComponent == BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO
+                || powerComponent == BatteryConsumer.POWER_COMPONENT_SCREEN
+                || powerComponent == BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY;
+    }
+
+    private static class ProportionalAttributionCalculator {
+        private static final double SYSTEM_BATTERY_CONSUMER = -1;
+        private final PackageManager mPackageManager;
+        private final HashSet<String> mSystemAndServicePackages;
+        private final SparseDoubleArray mProportionalPowerMah;
+
+        ProportionalAttributionCalculator(Context context, BatteryUsageStats stats) {
+            mPackageManager = context.getPackageManager();
+            final Resources resources = context.getResources();
+            final String[] systemPackageArray = resources.getStringArray(
+                    com.android.internal.R.array.config_batteryPackageTypeSystem);
+            final String[] servicePackageArray = resources.getStringArray(
+                    com.android.internal.R.array.config_batteryPackageTypeService);
+            mSystemAndServicePackages =
+                    new HashSet<>(systemPackageArray.length + servicePackageArray.length);
+            for (String packageName : systemPackageArray) {
+                mSystemAndServicePackages.add(packageName);
+            }
+            for (String packageName : servicePackageArray) {
+                mSystemAndServicePackages.add(packageName);
+            }
+
+            final List<UidBatteryConsumer> uidBatteryConsumers = stats.getUidBatteryConsumers();
+            mProportionalPowerMah =  new SparseDoubleArray(uidBatteryConsumers.size());
+            double systemPowerMah = 0;
+            for (int i = uidBatteryConsumers.size() - 1; i >= 0; i--) {
+                UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
+                final int uid = consumer.getUid();
+                if (isSystemUid(uid)) {
+                    mProportionalPowerMah.put(uid, SYSTEM_BATTERY_CONSUMER);
+                    systemPowerMah += consumer.getConsumedPower();
+                }
+            }
+
+            final double totalRemainingPower = stats.getConsumedPower() - systemPowerMah;
+            if (Math.abs(totalRemainingPower) > 1e-3) {
+                for (int i = uidBatteryConsumers.size() - 1; i >= 0; i--) {
+                    UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
+                    final int uid = consumer.getUid();
+                    if (mProportionalPowerMah.get(uid) != SYSTEM_BATTERY_CONSUMER) {
+                        final double power = consumer.getConsumedPower();
+                        mProportionalPowerMah.put(uid,
+                                power + systemPowerMah * power / totalRemainingPower);
+                    }
+                }
+            }
+        }
+
+        boolean isSystemBatteryConsumer(UidBatteryConsumer consumer) {
+            return mProportionalPowerMah.get(consumer.getUid()) < 0;
+        }
+
+        double getProportionalPowerMah(UidBatteryConsumer consumer) {
+            final double powerMah = mProportionalPowerMah.get(consumer.getUid());
+            return powerMah >= 0 ? powerMah : 0;
+        }
+
+        /**
+         * Check whether the UID is one of the system UIDs or a service UID
+         */
+        private boolean isSystemUid(int uid) {
+            if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
+                return true;
+            }
+
+            final String[] packages = mPackageManager.getPackagesForUid(uid);
+            if (packages == null) {
+                return false;
+            }
+
+            for (String packageName : packages) {
+                if (mSystemAndServicePackages.contains(packageName)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    private static class UidMobileRadioStats {
+        public final int uid;
+        public final long rxPackets;
+        public final long txPackets;
+        public final long radioActiveMs;
+        public final int radioActiveCount;
+        public final double millisecondsPerPacket;
+
+        private UidMobileRadioStats(int uid, long rxPackets, long txPackets, long radioActiveMs,
+                int radioActiveCount, double millisecondsPerPacket) {
+            this.uid = uid;
+            this.txPackets = txPackets;
+            this.rxPackets = rxPackets;
+            this.radioActiveMs = radioActiveMs;
+            this.radioActiveCount = radioActiveCount;
+            this.millisecondsPerPacket = millisecondsPerPacket;
+        }
+    }
+
+    private List<UidMobileRadioStats> getUidMobileRadioStats(
+            List<UidBatteryConsumer> uidBatteryConsumers) {
+        final SparseArray<? extends Uid> uidStats = getUidStats();
+        List<UidMobileRadioStats> uidMobileRadioStats = Lists.newArrayList();
+        for (int i = 0; i < uidBatteryConsumers.size(); i++) {
+            final UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
+            if (consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) == 0) {
+                continue;
+            }
+
+            final int uid = consumer.getUid();
+            final Uid u = uidStats.get(uid);
+            final long rxPackets = u.getNetworkActivityPackets(
+                    BatteryStats.NETWORK_MOBILE_RX_DATA, STATS_SINCE_CHARGED);
+            final long txPackets = u.getNetworkActivityPackets(
+                    BatteryStats.NETWORK_MOBILE_TX_DATA, STATS_SINCE_CHARGED);
+            if (rxPackets == 0 && txPackets == 0) {
+                continue;
+            }
+            final long radioActiveMs = u.getMobileRadioActiveTime(STATS_SINCE_CHARGED) / 1000;
+            final int radioActiveCount = u.getMobileRadioActiveCount(STATS_SINCE_CHARGED);
+            final double msPerPacket = (double) radioActiveMs / (rxPackets + txPackets);
+            if (msPerPacket == 0) {
+                continue;
+            }
+            uidMobileRadioStats.add(
+                    new UidMobileRadioStats(uid, rxPackets, txPackets, radioActiveMs,
+                            radioActiveCount, msPerPacket));
+        }
+        uidMobileRadioStats.sort(
+                (lhs, rhs) -> Double.compare(rhs.millisecondsPerPacket, lhs.millisecondsPerPacket));
+        return uidMobileRadioStats;
+    }
+}
diff --git a/android-34/android/os/BatteryStatsInternal.java b/android-34/android/os/BatteryStatsInternal.java
new file mode 100644
index 0000000..0713999
--- /dev/null
+++ b/android-34/android/os/BatteryStatsInternal.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 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.os;
+
+import android.annotation.IntDef;
+import android.net.Network;
+
+import com.android.internal.os.BinderCallsStats;
+import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Battery stats local system service interface. This is used to pass internal data out of
+ * BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl.
+ *
+ * @hide Only for use within Android OS.
+ */
+public abstract class BatteryStatsInternal {
+
+    public static final int CPU_WAKEUP_SUBSYSTEM_UNKNOWN = -1;
+    public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
+    public static final int CPU_WAKEUP_SUBSYSTEM_WIFI = 2;
+    public static final int CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER = 3;
+    public static final int CPU_WAKEUP_SUBSYSTEM_SENSOR = 4;
+    public static final int CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA = 5;
+
+    /** @hide */
+    @IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
+            CPU_WAKEUP_SUBSYSTEM_UNKNOWN,
+            CPU_WAKEUP_SUBSYSTEM_ALARM,
+            CPU_WAKEUP_SUBSYSTEM_WIFI,
+            CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+            CPU_WAKEUP_SUBSYSTEM_SENSOR,
+            CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CpuWakeupSubsystem {
+    }
+
+    /**
+     * Returns the wifi interfaces.
+     */
+    public abstract String[] getWifiIfaces();
+
+    /**
+     * Returns the mobile data interfaces.
+     */
+    public abstract String[] getMobileIfaces();
+
+    /** Returns CPU times for system server thread groups. */
+    public abstract SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes();
+
+    /**
+     * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+     * and per-UID basis.
+     *
+     * <p>
+     * Note: This is a slow running method and should be called from non-blocking threads only.
+     * </p>
+     */
+    public abstract List<BatteryUsageStats> getBatteryUsageStats(
+            List<BatteryUsageStatsQuery> queries);
+
+    /**
+     * Inform battery stats how many deferred jobs existed when the app got launched and how
+     * long ago was the last job execution for the app.
+     *
+     * @param uid         the uid of the app.
+     * @param numDeferred number of deferred jobs.
+     * @param sinceLast   how long in millis has it been since a job was run
+     */
+    public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast);
+
+    /**
+     * Informs battery stats of a data packet that woke up the CPU.
+     *
+     * @param network The network over which the packet arrived.
+     * @param elapsedMillis The time of the packet's arrival in elapsed timebase.
+     * @param uid The uid that received the packet.
+     */
+    public abstract void noteCpuWakingNetworkPacket(Network network, long elapsedMillis, int uid);
+
+    /**
+     * Informs battery stats of binder stats for the given work source UID.
+     */
+    public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
+            Collection<BinderCallsStats.CallStat> callStats);
+
+    /**
+     * Informs battery stats of native thread IDs of threads taking incoming binder calls.
+     */
+    public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
+
+    /**
+     * Reports a sound trigger recognition event that may have woken up the CPU.
+     * @param elapsedMillis The time when the event happened in the elapsed timebase.
+     * @param uid The uid that requested this trigger.
+     */
+    public abstract void noteWakingSoundTrigger(long elapsedMillis, int uid);
+
+    /**
+     * Reports an alarm batch that would have woken up the CPU.
+     * @param elapsedMillis The time at which this alarm batch was scheduled to go off.
+     * @param uids the uids of all apps that have any alarm in this batch.
+     */
+    public abstract void noteWakingAlarmBatch(long elapsedMillis, int... uids);
+}
diff --git a/android-34/android/os/BatteryStatsManager.java b/android-34/android/os/BatteryStatsManager.java
new file mode 100644
index 0000000..955fad3
--- /dev/null
+++ b/android-34/android/os/BatteryStatsManager.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.net.NetworkStack;
+import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.telephony.DataConnectionRealTimeInfo;
+
+import com.android.internal.app.IBatteryStats;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * This class provides an API surface for internal system components to report events that are
+ * needed for battery usage/estimation and battery blaming for apps.
+ *
+ * Note: This internally uses the same {@link IBatteryStats} binder service as the public
+ * {@link BatteryManager}.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.BATTERY_STATS_SERVICE)
+public final class BatteryStatsManager {
+    /**
+     * Wifi states.
+     *
+     * @see #noteWifiState(int, String)
+     */
+    /**
+     * Wifi fully off.
+     */
+    public static final int WIFI_STATE_OFF = 0;
+    /**
+     * Wifi connectivity off, but scanning enabled.
+     */
+    public static final int WIFI_STATE_OFF_SCANNING = 1;
+    /**
+     * Wifi on, but no saved infrastructure networks to connect to.
+     */
+    public static final int WIFI_STATE_ON_NO_NETWORKS = 2;
+    /**
+     * Wifi on, but not connected to any infrastructure networks.
+     */
+    public static final int WIFI_STATE_ON_DISCONNECTED = 3;
+    /**
+     * Wifi on and connected to a infrastructure network.
+     */
+    public static final int WIFI_STATE_ON_CONNECTED_STA = 4;
+    /**
+     * Wifi on and connected to a P2P device, but no infrastructure connection to a network.
+     */
+    public static final int WIFI_STATE_ON_CONNECTED_P2P = 5;
+    /**
+     * Wifi on and connected to both a P2P device and infrastructure connection to a network.
+     */
+    public static final int WIFI_STATE_ON_CONNECTED_STA_P2P = 6;
+    /**
+     * SoftAp/Hotspot turned on.
+     */
+    public static final int WIFI_STATE_SOFT_AP = 7;
+
+    /** @hide */
+    public static final int NUM_WIFI_STATES = WIFI_STATE_SOFT_AP + 1;
+
+    /** @hide */
+    @IntDef(prefix = { "WIFI_STATE_" }, value = {
+            WIFI_STATE_OFF,
+            WIFI_STATE_OFF_SCANNING,
+            WIFI_STATE_ON_NO_NETWORKS,
+            WIFI_STATE_ON_DISCONNECTED,
+            WIFI_STATE_ON_CONNECTED_STA,
+            WIFI_STATE_ON_CONNECTED_P2P,
+            WIFI_STATE_ON_CONNECTED_STA_P2P,
+            WIFI_STATE_SOFT_AP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiState {}
+
+    /**
+     * Wifi supplicant daemon states.
+     *
+     * @see android.net.wifi.SupplicantState for detailed description of states.
+     * @see #noteWifiSupplicantStateChanged(int)
+     */
+    /** @see android.net.wifi.SupplicantState#INVALID */
+    public static final int WIFI_SUPPL_STATE_INVALID = 0;
+    /** @see android.net.wifi.SupplicantState#DISCONNECTED*/
+    public static final int WIFI_SUPPL_STATE_DISCONNECTED = 1;
+    /** @see android.net.wifi.SupplicantState#INTERFACE_DISABLED */
+    public static final int WIFI_SUPPL_STATE_INTERFACE_DISABLED = 2;
+    /** @see android.net.wifi.SupplicantState#INACTIVE*/
+    public static final int WIFI_SUPPL_STATE_INACTIVE = 3;
+    /** @see android.net.wifi.SupplicantState#SCANNING*/
+    public static final int WIFI_SUPPL_STATE_SCANNING = 4;
+    /** @see android.net.wifi.SupplicantState#AUTHENTICATING */
+    public static final int WIFI_SUPPL_STATE_AUTHENTICATING = 5;
+    /** @see android.net.wifi.SupplicantState#ASSOCIATING */
+    public static final int WIFI_SUPPL_STATE_ASSOCIATING = 6;
+    /** @see android.net.wifi.SupplicantState#ASSOCIATED */
+    public static final int WIFI_SUPPL_STATE_ASSOCIATED = 7;
+    /** @see android.net.wifi.SupplicantState#FOUR_WAY_HANDSHAKE */
+    public static final int WIFI_SUPPL_STATE_FOUR_WAY_HANDSHAKE = 8;
+    /** @see android.net.wifi.SupplicantState#GROUP_HANDSHAKE */
+    public static final int WIFI_SUPPL_STATE_GROUP_HANDSHAKE = 9;
+    /** @see android.net.wifi.SupplicantState#COMPLETED */
+    public static final int WIFI_SUPPL_STATE_COMPLETED = 10;
+    /** @see android.net.wifi.SupplicantState#DORMANT */
+    public static final int WIFI_SUPPL_STATE_DORMANT = 11;
+    /** @see android.net.wifi.SupplicantState#UNINITIALIZED */
+    public static final int WIFI_SUPPL_STATE_UNINITIALIZED = 12;
+
+    /** @hide */
+    public static final int NUM_WIFI_SUPPL_STATES = WIFI_SUPPL_STATE_UNINITIALIZED + 1;
+
+    /** @hide */
+    @IntDef(prefix = { "WIFI_SUPPL_STATE_" }, value = {
+            WIFI_SUPPL_STATE_INVALID,
+            WIFI_SUPPL_STATE_DISCONNECTED,
+            WIFI_SUPPL_STATE_INTERFACE_DISABLED,
+            WIFI_SUPPL_STATE_INACTIVE,
+            WIFI_SUPPL_STATE_SCANNING,
+            WIFI_SUPPL_STATE_AUTHENTICATING,
+            WIFI_SUPPL_STATE_ASSOCIATING,
+            WIFI_SUPPL_STATE_ASSOCIATED,
+            WIFI_SUPPL_STATE_FOUR_WAY_HANDSHAKE,
+            WIFI_SUPPL_STATE_GROUP_HANDSHAKE,
+            WIFI_SUPPL_STATE_COMPLETED,
+            WIFI_SUPPL_STATE_DORMANT,
+            WIFI_SUPPL_STATE_UNINITIALIZED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiSupplState {}
+
+
+    private final IBatteryStats mBatteryStats;
+
+    /** @hide */
+    public BatteryStatsManager(IBatteryStats batteryStats) {
+        mBatteryStats = batteryStats;
+    }
+
+
+    /**
+     * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+     * and per-UID basis.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public BatteryUsageStats getBatteryUsageStats() {
+        return getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT);
+    }
+
+    /**
+     * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+     * and per-UID basis.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+        return getBatteryUsageStats(List.of(query)).get(0);
+    }
+
+    /**
+     * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+     * and per-UID basis.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
+        try {
+            return mBatteryStats.getBatteryUsageStats(queries);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that the wifi connection RSSI has changed.
+     *
+     * @param newRssi The new RSSI value.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiRssiChanged(@IntRange(from = -127, to = 0) int newRssi) {
+        try {
+            mBatteryStats.noteWifiRssiChanged(newRssi);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that wifi was toggled on.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiOn() {
+        try {
+            mBatteryStats.noteWifiOn();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that wifi was toggled off.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiOff() {
+        try {
+            mBatteryStats.noteWifiOff();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that wifi state has changed.
+     *
+     * @param newWifiState The new wifi State.
+     * @param accessPoint SSID of the network if wifi is connected to STA, else null.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiState(@WifiState int newWifiState,
+            @Nullable String accessPoint) {
+        try {
+            mBatteryStats.noteWifiState(newWifiState, accessPoint);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that a new wifi scan has started.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiScanStartedFromSource(@NonNull WorkSource ws) {
+        try {
+            mBatteryStats.noteWifiScanStartedFromSource(ws);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that an ongoing wifi scan has stopped.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiScanStoppedFromSource(@NonNull WorkSource ws) {
+        try {
+            mBatteryStats.noteWifiScanStoppedFromSource(ws);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that a new wifi batched scan has started.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     * @param csph Channels scanned per hour.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiBatchedScanStartedFromSource(@NonNull WorkSource ws,
+            @IntRange(from = 0) int csph) {
+        try {
+            mBatteryStats.noteWifiBatchedScanStartedFromSource(ws, csph);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that an ongoing wifi batched scan has stopped.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
+        try {
+            mBatteryStats.noteWifiBatchedScanStoppedFromSource(ws);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves all the cellular related battery stats.
+     *
+     * @return Instance of {@link CellularBatteryStats}.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.BATTERY_STATS,
+            android.Manifest.permission.UPDATE_DEVICE_STATS})
+    public @NonNull CellularBatteryStats getCellularBatteryStats() {
+        try {
+            return mBatteryStats.getCellularBatteryStats();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Retrieves all the wifi related battery stats.
+     *
+     * @return Instance of {@link WifiBatteryStats}.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.BATTERY_STATS,
+            android.Manifest.permission.UPDATE_DEVICE_STATS})
+    public @NonNull WifiBatteryStats getWifiBatteryStats() {
+        try {
+            return mBatteryStats.getWifiBatteryStats();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Retrieves accumulate wake lock stats.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public WakeLockStats getWakeLockStats() {
+        try {
+            return mBatteryStats.getWakeLockStats();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves accumulated bluetooth stats.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public BluetoothBatteryStats getBluetoothBatteryStats() {
+        try {
+            return mBatteryStats.getBluetoothBatteryStats();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates an app acquiring full wifi lock.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
+        try {
+            mBatteryStats.noteFullWifiLockAcquiredFromSource(ws);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates an app releasing full wifi lock.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
+        try {
+            mBatteryStats.noteFullWifiLockReleasedFromSource(ws);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that supplicant state has changed.
+     *
+     * @param newSupplState The new Supplicant state.
+     * @param failedAuth Boolean indicating whether there was a connection failure due to
+     *                   authentication failure.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiSupplicantStateChanged(@WifiSupplState int newSupplState,
+            boolean failedAuth) {
+        try {
+            mBatteryStats.noteWifiSupplicantStateChanged(newSupplState, failedAuth);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that an app has acquired the wifi multicast lock.
+     *
+     * @param ws Worksource with the uid of the app that acquired the wifi lock (to be used for
+     *           battery blaming).
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiMulticastEnabled(@NonNull WorkSource ws) {
+        try {
+            mBatteryStats.noteWifiMulticastEnabled(ws.getAttributionUid());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that an app has released the wifi multicast lock.
+     *
+     * @param ws Worksource with the uid of the app that released the wifi lock (to be used for
+     *           battery blaming).
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiMulticastDisabled(@NonNull WorkSource ws) {
+        try {
+            mBatteryStats.noteWifiMulticastDisabled(ws.getAttributionUid());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that the radio power state has changed.
+     *
+     * @param isActive indicates if the mobile radio is powered.
+     * @param uid Uid of this event. For the active state it represents the uid that was responsible
+     *            for waking the radio, or -1 if the system was responsible for waking the radio.
+     *            For inactive state, the UID should always be -1.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportMobileRadioPowerState(boolean isActive, int uid) {
+        try {
+            mBatteryStats.noteMobileRadioPowerState(getDataConnectionPowerState(isActive),
+                    SystemClock.elapsedRealtimeNanos(), uid);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that the wifi power state has changed.
+     *
+     * @param isActive indicates if the wifi radio is powered.
+     * @param uid Uid of this event. For the active state it represents the uid that was responsible
+     *            for waking the radio, or -1 if the system was responsible for waking the radio.
+     *            For inactive state, the UID should always be -1.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiRadioPowerState(boolean isActive, int uid) {
+        try {
+            mBatteryStats.noteWifiRadioPowerState(getDataConnectionPowerState(isActive),
+                    SystemClock.elapsedRealtimeNanos(), uid);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the battery stats of a new interface, and the transport types of the network that
+     * includes that interface.
+     *
+     * @param iface The interface of the network.
+     * @param transportTypes The transport type of the network {@link Transport}.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void reportNetworkInterfaceForTransports(@NonNull String iface,
+            @NonNull int[] transportTypes) throws RuntimeException {
+        try {
+            mBatteryStats.noteNetworkInterfaceForTransports(iface, transportTypes);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that Bluetooth was toggled on.
+     *
+     * @param uid calling package uid
+     * @param reason why Bluetooth has been turned on
+     * @param packageName package responsible for this change
+     * @Deprecated Bluetooth self report its state and no longer call this
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+    public void reportBluetoothOn(int uid, int reason, @NonNull String packageName) {
+    }
+
+    /**
+     * Indicates that Bluetooth was toggled off.
+     *
+     * @param uid calling package uid
+     * @param reason why Bluetooth has been turned on
+     * @param packageName package responsible for this change
+     * @Deprecated Bluetooth self report its state and no longer call this
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+    public void reportBluetoothOff(int uid, int reason, @NonNull String packageName) {
+    }
+
+    /**
+     * Indicates that a new Bluetooth LE scan has started.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     * @param isUnoptimized whether or not the scan has a filter.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanStarted(@NonNull WorkSource ws, boolean isUnoptimized) {
+        try {
+            mBatteryStats.noteBleScanStarted(ws, isUnoptimized);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that an ongoing Bluetooth LE scan has stopped.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     * @param isUnoptimized whether or not the scan has a filter.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanStopped(@NonNull WorkSource ws, boolean isUnoptimized) {
+        try {
+            mBatteryStats.noteBleScanStopped(ws, isUnoptimized);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that Bluetooth LE has been reset.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanReset() {
+        try {
+            mBatteryStats.noteBleScanReset();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that Bluetooth LE scan has received new results.
+     *
+     * @param ws worksource (to be used for battery blaming).
+     * @param numNewResults number of results received since last update.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportBleScanResults(@NonNull WorkSource ws, int numNewResults) {
+        try {
+            mBatteryStats.noteBleScanResults(ws, numNewResults);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    private static int getDataConnectionPowerState(boolean isActive) {
+        // TODO: DataConnectionRealTimeInfo is under telephony package but the constants are used
+        // for both Wifi and mobile. It would make more sense to separate the constants to a
+        // generic class or move it to generic package.
+        return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+                : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+    }
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+        try {
+            mBatteryStats.setChargerAcOnline(online, forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void setBatteryLevel(int level, boolean forceUpdate) {
+        try {
+            mBatteryStats.setBatteryLevel(level, forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void unplugBattery(boolean forceUpdate) {
+        try {
+            mBatteryStats.unplugBattery(forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void resetBattery(boolean forceUpdate) {
+        try {
+            mBatteryStats.resetBattery(forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Suspend charging even if plugged in.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void suspendBatteryInput() {
+        try {
+            mBatteryStats.suspendBatteryInput();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+}
\ No newline at end of file
diff --git a/android-34/android/os/BatteryUsageStats.java b/android-34/android/os/BatteryUsageStats.java
new file mode 100644
index 0000000..e2c52ce
--- /dev/null
+++ b/android-34/android/os/BatteryUsageStats.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.util.Range;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis.
+ * <p>
+ * The totals for the entire device are returned as AggregateBatteryConsumers, which can be
+ * obtained by calling {@link #getAggregateBatteryConsumer(int)}.
+ * <p>
+ * Power attributed to individual apps is returned as UidBatteryConsumers, see
+ * {@link #getUidBatteryConsumers()}.
+ *
+ * @hide
+ */
+public final class BatteryUsageStats implements Parcelable, Closeable {
+
+    /**
+     * Scope of battery stats included in a BatteryConsumer: the entire device, just
+     * the apps, etc.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"AGGREGATE_BATTERY_CONSUMER_SCOPE_"}, value = {
+            AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+            AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public static @interface AggregateBatteryConsumerScope {
+    }
+
+    /**
+     * Power consumption by the entire device, since last charge.  The power usage in this
+     * scope includes both the power attributed to apps and the power unattributed to any
+     * apps.
+     */
+    public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE = 0;
+
+    /**
+     * Aggregated power consumed by all applications, combined, since last charge. This is
+     * the sum of power reported in UidBatteryConsumers.
+     */
+    public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS = 1;
+
+    public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
+
+    // XML tags and attributes for BatteryUsageStats persistence
+    static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
+    static final String XML_TAG_AGGREGATE = "aggregate";
+    static final String XML_TAG_UID = "uid";
+    static final String XML_TAG_USER = "user";
+    static final String XML_TAG_POWER_COMPONENTS = "power_components";
+    static final String XML_TAG_COMPONENT = "component";
+    static final String XML_TAG_CUSTOM_COMPONENT = "custom_component";
+    static final String XML_ATTR_ID = "id";
+    static final String XML_ATTR_UID = "uid";
+    static final String XML_ATTR_USER_ID = "user_id";
+    static final String XML_ATTR_SCOPE = "scope";
+    static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_";
+    static final String XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA = "includes_proc_state_data";
+    static final String XML_ATTR_START_TIMESTAMP = "start_timestamp";
+    static final String XML_ATTR_END_TIMESTAMP = "end_timestamp";
+    static final String XML_ATTR_PROCESS_STATE = "process_state";
+    static final String XML_ATTR_POWER = "power";
+    static final String XML_ATTR_DURATION = "duration";
+    static final String XML_ATTR_MODEL = "model";
+    static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity";
+    static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct";
+    static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower";
+    static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper";
+    static final String XML_ATTR_DISCHARGE_DURATION = "discharge_duration";
+    static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining";
+    static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining";
+    static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package";
+    static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
+    static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
+
+    // We need about 700 bytes per UID
+    private static final long BATTERY_CONSUMER_CURSOR_WINDOW_SIZE = 5_000 * 700;
+
+    private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+
+    private final int mDischargePercentage;
+    private final double mBatteryCapacityMah;
+    private final long mStatsStartTimestampMs;
+    private final long mStatsEndTimestampMs;
+    private final long mStatsDurationMs;
+    private final double mDischargedPowerLowerBound;
+    private final double mDischargedPowerUpperBound;
+    private final long mDischargeDurationMs;
+    private final long mBatteryTimeRemainingMs;
+    private final long mChargeTimeRemainingMs;
+    private final String[] mCustomPowerComponentNames;
+    private final boolean mIncludesPowerModels;
+    private final boolean mIncludesProcessStateData;
+    private final List<UidBatteryConsumer> mUidBatteryConsumers;
+    private final List<UserBatteryConsumer> mUserBatteryConsumers;
+    private final AggregateBatteryConsumer[] mAggregateBatteryConsumers;
+    private final BatteryStatsHistory mBatteryStatsHistory;
+    private CursorWindow mBatteryConsumersCursorWindow;
+
+    private BatteryUsageStats(@NonNull Builder builder) {
+        mStatsStartTimestampMs = builder.mStatsStartTimestampMs;
+        mStatsEndTimestampMs = builder.mStatsEndTimestampMs;
+        mStatsDurationMs = builder.getStatsDuration();
+        mBatteryCapacityMah = builder.mBatteryCapacityMah;
+        mDischargePercentage = builder.mDischargePercentage;
+        mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
+        mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
+        mDischargeDurationMs = builder.mDischargeDurationMs;
+        mBatteryStatsHistory = builder.mBatteryStatsHistory;
+        mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
+        mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
+        mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
+        mIncludesPowerModels = builder.mIncludePowerModels;
+        mIncludesProcessStateData = builder.mIncludesProcessStateData;
+        mBatteryConsumersCursorWindow = builder.mBatteryConsumersCursorWindow;
+
+        double totalPowerMah = 0;
+        final int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size();
+        mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount);
+        for (int i = 0; i < uidBatteryConsumerCount; i++) {
+            final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
+                    builder.mUidBatteryConsumerBuilders.valueAt(i);
+            if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) {
+                final UidBatteryConsumer consumer = uidBatteryConsumerBuilder.build();
+                totalPowerMah += consumer.getConsumedPower();
+                mUidBatteryConsumers.add(consumer);
+            }
+        }
+
+        final int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size();
+        mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount);
+        for (int i = 0; i < userBatteryConsumerCount; i++) {
+            final UserBatteryConsumer consumer =
+                    builder.mUserBatteryConsumerBuilders.valueAt(i).build();
+            totalPowerMah += consumer.getConsumedPower();
+            mUserBatteryConsumers.add(consumer);
+        }
+
+        builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+                .setConsumedPower(totalPowerMah);
+
+        mAggregateBatteryConsumers =
+                new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
+        for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
+            mAggregateBatteryConsumers[i] = builder.mAggregateBatteryConsumersBuilders[i].build();
+        }
+    }
+
+    /**
+     * Timestamp (as returned by System.currentTimeMillis()) of the latest battery stats reset, in
+     * milliseconds.
+     */
+    public long getStatsStartTimestamp() {
+        return mStatsStartTimestampMs;
+    }
+
+    /**
+     * Timestamp (as returned by System.currentTimeMillis()) of when the stats snapshot was taken,
+     * in milliseconds.
+     */
+    public long getStatsEndTimestamp() {
+        return mStatsEndTimestampMs;
+    }
+
+    /**
+     * Returns the duration of the stats session captured by this BatteryUsageStats.
+     * In rare cases, statsDuration != statsEndTimestamp - statsStartTimestamp.  This may
+     * happen when BatteryUsageStats represents an accumulation of data across multiple
+     * non-contiguous sessions.
+     */
+    public long getStatsDuration() {
+        return mStatsDurationMs;
+    }
+
+    /**
+     * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
+     * charged), in mAh
+     */
+    public double getConsumedPower() {
+        return mAggregateBatteryConsumers[AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE]
+                .getConsumedPower();
+    }
+
+    /**
+     * Returns battery capacity in milli-amp-hours.
+     */
+    public double getBatteryCapacity() {
+        return mBatteryCapacityMah;
+    }
+
+    /**
+     * Portion of battery charge drained since BatteryStats reset (e.g. due to being fully
+     * charged), as percentage of the full charge in the range [0:100]. May exceed 100 if
+     * the device repeatedly charged and discharged prior to the reset.
+     */
+    public int getDischargePercentage() {
+        return mDischargePercentage;
+    }
+
+    /**
+     * Returns the discharged power since BatteryStats were last reset, in mAh as an estimated
+     * range.
+     */
+    public Range<Double> getDischargedPowerRange() {
+        return Range.create(mDischargedPowerLowerBound, mDischargedPowerUpperBound);
+    }
+
+    /**
+     * Returns the total amount of time the battery was discharging.
+     */
+    public long getDischargeDurationMs() {
+        return mDischargeDurationMs;
+    }
+
+    /**
+     * Returns an approximation for how much run time (in milliseconds) is remaining on
+     * the battery.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently
+     * charging.
+     */
+    public long getBatteryTimeRemainingMs() {
+        return mBatteryTimeRemainingMs;
+    }
+
+    /**
+     * Returns an approximation for how much time (in milliseconds) remains until the battery
+     * is fully charged.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently discharging.
+     */
+    public long getChargeTimeRemainingMs() {
+        return mChargeTimeRemainingMs;
+    }
+
+    /**
+     * Returns a battery consumer for the specified battery consumer type.
+     */
+    public AggregateBatteryConsumer getAggregateBatteryConsumer(
+            @AggregateBatteryConsumerScope int scope) {
+        return mAggregateBatteryConsumers[scope];
+    }
+
+    @NonNull
+    public List<UidBatteryConsumer> getUidBatteryConsumers() {
+        return mUidBatteryConsumers;
+    }
+
+    @NonNull
+    public List<UserBatteryConsumer> getUserBatteryConsumers() {
+        return mUserBatteryConsumers;
+    }
+
+    /**
+     * Returns the names of custom power components in order, so the first name in the array
+     * corresponds to the custom componentId
+     * {@link BatteryConsumer#FIRST_CUSTOM_POWER_COMPONENT_ID}.
+     */
+    @NonNull
+    public String[] getCustomPowerComponentNames() {
+        return mCustomPowerComponentNames;
+    }
+
+    public boolean isProcessStateDataIncluded() {
+        return mIncludesProcessStateData;
+    }
+
+    /**
+     * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s.
+     */
+    @NonNull
+    public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
+        if (mBatteryStatsHistory == null) {
+            throw new IllegalStateException(
+                    "Battery history was not requested in the BatteryUsageStatsQuery");
+        }
+        return new BatteryStatsHistoryIterator(mBatteryStatsHistory);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private BatteryUsageStats(@NonNull Parcel source) {
+        mStatsStartTimestampMs = source.readLong();
+        mStatsEndTimestampMs = source.readLong();
+        mStatsDurationMs = source.readLong();
+        mBatteryCapacityMah = source.readDouble();
+        mDischargePercentage = source.readInt();
+        mDischargedPowerLowerBound = source.readDouble();
+        mDischargedPowerUpperBound = source.readDouble();
+        mDischargeDurationMs = source.readLong();
+        mBatteryTimeRemainingMs = source.readLong();
+        mChargeTimeRemainingMs = source.readLong();
+        mCustomPowerComponentNames = source.readStringArray();
+        mIncludesPowerModels = source.readBoolean();
+        mIncludesProcessStateData = source.readBoolean();
+
+        mBatteryConsumersCursorWindow = CursorWindow.newFromParcel(source);
+        BatteryConsumer.BatteryConsumerDataLayout dataLayout =
+                BatteryConsumer.createBatteryConsumerDataLayout(mCustomPowerComponentNames,
+                        mIncludesPowerModels, mIncludesProcessStateData);
+
+        final int numRows = mBatteryConsumersCursorWindow.getNumRows();
+
+        mAggregateBatteryConsumers =
+                new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
+        mUidBatteryConsumers = new ArrayList<>(numRows);
+        mUserBatteryConsumers = new ArrayList<>();
+
+        for (int i = 0; i < numRows; i++) {
+            final BatteryConsumer.BatteryConsumerData data =
+                    new BatteryConsumer.BatteryConsumerData(mBatteryConsumersCursorWindow, i,
+                            dataLayout);
+
+            int consumerType = mBatteryConsumersCursorWindow.getInt(i,
+                            BatteryConsumer.COLUMN_INDEX_BATTERY_CONSUMER_TYPE);
+            switch (consumerType) {
+                case AggregateBatteryConsumer.CONSUMER_TYPE_AGGREGATE: {
+                    final AggregateBatteryConsumer consumer = new AggregateBatteryConsumer(data);
+                    mAggregateBatteryConsumers[consumer.getScope()] = consumer;
+                    break;
+                }
+                case UidBatteryConsumer.CONSUMER_TYPE_UID: {
+                    mUidBatteryConsumers.add(new UidBatteryConsumer(data));
+                    break;
+                }
+                case UserBatteryConsumer.CONSUMER_TYPE_USER:
+                    mUserBatteryConsumers.add(new UserBatteryConsumer(data));
+                    break;
+            }
+        }
+
+        if (source.readBoolean()) {
+            mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source);
+        } else {
+            mBatteryStatsHistory = null;
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mStatsStartTimestampMs);
+        dest.writeLong(mStatsEndTimestampMs);
+        dest.writeLong(mStatsDurationMs);
+        dest.writeDouble(mBatteryCapacityMah);
+        dest.writeInt(mDischargePercentage);
+        dest.writeDouble(mDischargedPowerLowerBound);
+        dest.writeDouble(mDischargedPowerUpperBound);
+        dest.writeLong(mDischargeDurationMs);
+        dest.writeLong(mBatteryTimeRemainingMs);
+        dest.writeLong(mChargeTimeRemainingMs);
+        dest.writeStringArray(mCustomPowerComponentNames);
+        dest.writeBoolean(mIncludesPowerModels);
+        dest.writeBoolean(mIncludesProcessStateData);
+
+        mBatteryConsumersCursorWindow.writeToParcel(dest, flags);
+
+        if (mBatteryStatsHistory != null) {
+            dest.writeBoolean(true);
+            mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest);
+        } else {
+            dest.writeBoolean(false);
+        }
+    }
+
+    @NonNull
+    public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() {
+        public BatteryUsageStats createFromParcel(@NonNull Parcel source) {
+            return new BatteryUsageStats(source);
+        }
+
+        public BatteryUsageStats[] newArray(int size) {
+            return new BatteryUsageStats[size];
+        }
+    };
+
+    /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */
+    public byte[] getStatsProto() {
+        // ProtoOutputStream.getRawSize() returns the buffer size before compaction.
+        // BatteryUsageStats contains a lot of integers, so compaction of integers to
+        // varint reduces the size of the proto buffer by as much as 50%.
+        int maxRawSize = (int) (STATSD_PULL_ATOM_MAX_BYTES * 1.75);
+        // Limit the number of attempts in order to prevent an infinite loop
+        for (int i = 0; i < 3; i++) {
+            final ProtoOutputStream proto = new ProtoOutputStream();
+            writeStatsProto(proto, maxRawSize);
+
+            final int rawSize = proto.getRawSize();
+            final byte[] protoOutput = proto.getBytes();
+
+            if (protoOutput.length <= STATSD_PULL_ATOM_MAX_BYTES) {
+                return protoOutput;
+            }
+
+            // Adjust maxRawSize proportionately and try again.
+            maxRawSize =
+                    (int) ((long) STATSD_PULL_ATOM_MAX_BYTES * rawSize / protoOutput.length - 1024);
+        }
+
+        // Fallback: if we have failed to generate a proto smaller than STATSD_PULL_ATOM_MAX_BYTES,
+        // just generate a proto with the _rawSize_ of STATSD_PULL_ATOM_MAX_BYTES, which is
+        // guaranteed to produce a compacted proto (significantly) smaller than
+        // STATSD_PULL_ATOM_MAX_BYTES.
+        final ProtoOutputStream proto = new ProtoOutputStream();
+        writeStatsProto(proto, STATSD_PULL_ATOM_MAX_BYTES);
+        return proto.getBytes();
+    }
+
+    /**
+     * Writes contents in a binary protobuffer format, using
+     * the android.os.BatteryUsageStatsAtomsProto proto.
+     */
+    public void dumpToProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        writeStatsProto(proto, /* max size */ Integer.MAX_VALUE);
+        proto.flush();
+    }
+
+    @NonNull
+    private void writeStatsProto(ProtoOutputStream proto, int maxRawSize) {
+        final AggregateBatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
+                AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+
+        proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp());
+        proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp());
+        proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration());
+        proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE,
+                getDischargePercentage());
+        proto.write(BatteryUsageStatsAtomsProto.DISCHARGE_DURATION_MILLIS,
+                getDischargeDurationMs());
+        deviceBatteryConsumer.writeStatsProto(proto,
+                BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
+        if (mIncludesPowerModels) {
+            deviceBatteryConsumer.writePowerComponentModelProto(proto);
+        }
+        writeUidBatteryConsumersProto(proto, maxRawSize);
+    }
+
+    /**
+     * Writes the UidBatteryConsumers data, held by this BatteryUsageStats, to the proto (as used
+     * for atoms.proto).
+     */
+    private void writeUidBatteryConsumersProto(ProtoOutputStream proto, int maxRawSize) {
+        final List<UidBatteryConsumer> consumers = getUidBatteryConsumers();
+        // Order consumers by descending weight (a combination of consumed power and usage time)
+        consumers.sort(Comparator.comparingDouble(this::getUidBatteryConsumerWeight).reversed());
+
+        final int size = consumers.size();
+        for (int i = 0; i < size; i++) {
+            final UidBatteryConsumer consumer = consumers.get(i);
+
+            final long fgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
+            final long bgMs = consumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
+            final boolean hasBaseData = consumer.hasStatsProtoData();
+
+            if (fgMs == 0 && bgMs == 0 && !hasBaseData) {
+                continue;
+            }
+
+            final long token = proto.start(BatteryUsageStatsAtomsProto.UID_BATTERY_CONSUMERS);
+            proto.write(
+                    BatteryUsageStatsAtomsProto.UidBatteryConsumer.UID,
+                    consumer.getUid());
+            if (hasBaseData) {
+                consumer.writeStatsProto(proto,
+                        BatteryUsageStatsAtomsProto.UidBatteryConsumer.BATTERY_CONSUMER_DATA);
+            }
+            proto.write(
+                    BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_FOREGROUND_MILLIS,
+                    fgMs);
+            proto.write(
+                    BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS,
+                    bgMs);
+            proto.end(token);
+
+            if (proto.getRawSize() >= maxRawSize) {
+                break;
+            }
+        }
+    }
+
+    private static final double WEIGHT_CONSUMED_POWER = 1;
+    // Weight one hour in foreground the same as 100 mAh of power drain
+    private static final double WEIGHT_FOREGROUND_STATE = 100.0 / (1 * 60 * 60 * 1000);
+    // Weight one hour in background the same as 300 mAh of power drain
+    private static final double WEIGHT_BACKGROUND_STATE = 300.0 / (1 * 60 * 60 * 1000);
+
+    /**
+     * Computes the weight associated with a UidBatteryConsumer, which is used for sorting.
+     * We want applications with the largest consumed power as well as applications
+     * with the highest usage time to be included in the statsd atom.
+     */
+    private double getUidBatteryConsumerWeight(UidBatteryConsumer uidBatteryConsumer) {
+        final double consumedPower = uidBatteryConsumer.getConsumedPower();
+        final long timeInForeground =
+                uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
+        final long timeInBackground =
+                uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
+        return consumedPower * WEIGHT_CONSUMED_POWER
+                + timeInForeground * WEIGHT_FOREGROUND_STATE
+                + timeInBackground * WEIGHT_BACKGROUND_STATE;
+    }
+
+    /**
+     * Prints the stats in a human-readable format.
+     */
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix);
+        pw.println("  Estimated power use (mAh):");
+        pw.print(prefix);
+        pw.print("    Capacity: ");
+        pw.print(BatteryStats.formatCharge(getBatteryCapacity()));
+        pw.print(", Computed drain: ");
+        pw.print(BatteryStats.formatCharge(getConsumedPower()));
+        final Range<Double> dischargedPowerRange = getDischargedPowerRange();
+        pw.print(", actual drain: ");
+        pw.print(BatteryStats.formatCharge(dischargedPowerRange.getLower()));
+        if (!dischargedPowerRange.getLower().equals(dischargedPowerRange.getUpper())) {
+            pw.print("-");
+            pw.print(BatteryStats.formatCharge(dischargedPowerRange.getUpper()));
+        }
+        pw.println();
+
+        pw.println("    Global");
+        final BatteryConsumer deviceConsumer = getAggregateBatteryConsumer(
+                AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+        final BatteryConsumer appsConsumer = getAggregateBatteryConsumer(
+                AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+
+        for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+                componentId++) {
+            for (BatteryConsumer.Key key : deviceConsumer.getKeys(componentId)) {
+                final double devicePowerMah = deviceConsumer.getConsumedPower(key);
+                final double appsPowerMah = appsConsumer.getConsumedPower(key);
+                if (devicePowerMah == 0 && appsPowerMah == 0) {
+                    continue;
+                }
+
+                String label = BatteryConsumer.powerComponentIdToString(componentId);
+                if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                    label = label
+                            + "(" + BatteryConsumer.processStateToString(key.processState) + ")";
+                }
+                printPowerComponent(pw, prefix, label, devicePowerMah, appsPowerMah,
+                        deviceConsumer.getPowerModel(key),
+                        deviceConsumer.getUsageDurationMillis(key));
+            }
+        }
+
+        for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+                componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+                        + mCustomPowerComponentNames.length;
+                componentId++) {
+            final double devicePowerMah =
+                    deviceConsumer.getConsumedPowerForCustomComponent(componentId);
+            final double appsPowerMah =
+                    appsConsumer.getConsumedPowerForCustomComponent(componentId);
+            if (devicePowerMah == 0 && appsPowerMah == 0) {
+                continue;
+            }
+
+            printPowerComponent(pw, prefix, deviceConsumer.getCustomPowerComponentName(componentId),
+                    devicePowerMah, appsPowerMah,
+                    BatteryConsumer.POWER_MODEL_UNDEFINED,
+                    deviceConsumer.getUsageDurationForCustomComponentMillis(componentId));
+        }
+
+        dumpSortedBatteryConsumers(pw, prefix, getUidBatteryConsumers());
+        dumpSortedBatteryConsumers(pw, prefix, getUserBatteryConsumers());
+        pw.println();
+    }
+
+    private void printPowerComponent(PrintWriter pw, String prefix, String label,
+            double devicePowerMah, double appsPowerMah, int powerModel, long durationMs) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(prefix).append("      ").append(label).append(": ")
+                .append(BatteryStats.formatCharge(devicePowerMah));
+        if (powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED
+                && powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
+            sb.append(" [");
+            sb.append(BatteryConsumer.powerModelToString(powerModel));
+            sb.append("]");
+        }
+        sb.append(" apps: ").append(BatteryStats.formatCharge(appsPowerMah));
+        if (durationMs != 0) {
+            sb.append(" duration: ");
+            BatteryStats.formatTimeMs(sb, durationMs);
+        }
+
+        pw.println(sb.toString());
+    }
+
+    private void dumpSortedBatteryConsumers(PrintWriter pw, String prefix,
+            List<? extends BatteryConsumer> batteryConsumers) {
+        batteryConsumers.sort(
+                Comparator.<BatteryConsumer>comparingDouble(BatteryConsumer::getConsumedPower)
+                        .reversed());
+        for (BatteryConsumer consumer : batteryConsumers) {
+            if (consumer.getConsumedPower() == 0) {
+                continue;
+            }
+            pw.print(prefix);
+            pw.print("    ");
+            consumer.dump(pw);
+            pw.println();
+        }
+    }
+
+    /** Serializes this object to XML */
+    public void writeXml(TypedXmlSerializer serializer) throws IOException {
+        serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS);
+
+        for (int i = 0; i < mCustomPowerComponentNames.length; i++) {
+            serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i,
+                    mCustomPowerComponentNames[i]);
+        }
+        serializer.attributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA,
+                mIncludesProcessStateData);
+        serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs);
+        serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs);
+        serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs);
+        serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah);
+        serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage);
+        serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound);
+        serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound);
+        serializer.attributeLong(null, XML_ATTR_DISCHARGE_DURATION, mDischargeDurationMs);
+        serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs);
+        serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs);
+
+        for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
+                scope++) {
+            mAggregateBatteryConsumers[scope].writeToXml(serializer, scope);
+        }
+        for (UidBatteryConsumer consumer : mUidBatteryConsumers) {
+            consumer.writeToXml(serializer);
+        }
+        for (UserBatteryConsumer consumer : mUserBatteryConsumers) {
+            consumer.writeToXml(serializer);
+        }
+        serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS);
+    }
+
+    /** Parses an XML representation of BatteryUsageStats */
+    public static BatteryUsageStats createFromXml(TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        Builder builder = null;
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG
+                    && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) {
+                List<String> customComponentNames = new ArrayList<>();
+                int i = 0;
+                while (true) {
+                    int index = parser.getAttributeIndex(null,
+                            XML_ATTR_PREFIX_CUSTOM_COMPONENT + i);
+                    if (index == -1) {
+                        break;
+                    }
+                    customComponentNames.add(parser.getAttributeValue(index));
+                    i++;
+                }
+
+                final boolean includesProcStateData = parser.getAttributeBoolean(null,
+                        XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA, false);
+
+                builder = new Builder(customComponentNames.toArray(new String[0]), true,
+                        includesProcStateData);
+
+                builder.setStatsStartTimestamp(
+                        parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP));
+                builder.setStatsEndTimestamp(
+                        parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP));
+                builder.setStatsDuration(
+                        parser.getAttributeLong(null, XML_ATTR_DURATION));
+                builder.setBatteryCapacity(
+                        parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY));
+                builder.setDischargePercentage(
+                        parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT));
+                builder.setDischargedPowerRange(
+                        parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER),
+                        parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER));
+                builder.setDischargeDurationMs(
+                        parser.getAttributeLong(null, XML_ATTR_DISCHARGE_DURATION));
+                builder.setBatteryTimeRemainingMs(
+                        parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING));
+                builder.setChargeTimeRemainingMs(
+                        parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING));
+
+                eventType = parser.next();
+                break;
+            }
+            eventType = parser.next();
+        }
+
+        if (builder == null) {
+            throw new XmlPullParserException("No root element");
+        }
+
+        while (eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG) {
+                switch (parser.getName()) {
+                    case XML_TAG_AGGREGATE:
+                        AggregateBatteryConsumer.parseXml(parser, builder);
+                        break;
+                    case XML_TAG_UID:
+                        UidBatteryConsumer.createFromXml(parser, builder);
+                        break;
+                    case XML_TAG_USER:
+                        UserBatteryConsumer.createFromXml(parser, builder);
+                        break;
+                }
+            }
+            eventType = parser.next();
+        }
+
+        return builder.build();
+    }
+
+    @Override
+    public void close() throws IOException {
+        mBatteryConsumersCursorWindow.close();
+        mBatteryConsumersCursorWindow = null;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (mBatteryConsumersCursorWindow != null) {
+            mBatteryConsumersCursorWindow.close();
+        }
+        super.finalize();
+    }
+
+    /**
+     * Builder for BatteryUsageStats.
+     */
+    public static final class Builder {
+        private final CursorWindow mBatteryConsumersCursorWindow;
+        @NonNull
+        private final String[] mCustomPowerComponentNames;
+        private final boolean mIncludePowerModels;
+        private final boolean mIncludesProcessStateData;
+        private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout;
+        private long mStatsStartTimestampMs;
+        private long mStatsEndTimestampMs;
+        private long mStatsDurationMs = -1;
+        private double mBatteryCapacityMah;
+        private int mDischargePercentage;
+        private double mDischargedPowerLowerBoundMah;
+        private double mDischargedPowerUpperBoundMah;
+        private long mDischargeDurationMs;
+        private long mBatteryTimeRemainingMs = -1;
+        private long mChargeTimeRemainingMs = -1;
+        private final AggregateBatteryConsumer.Builder[] mAggregateBatteryConsumersBuilders =
+                new AggregateBatteryConsumer.Builder[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
+        private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
+                new SparseArray<>();
+        private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
+                new SparseArray<>();
+        private BatteryStatsHistory mBatteryStatsHistory;
+
+        public Builder(@NonNull String[] customPowerComponentNames) {
+            this(customPowerComponentNames, false, false);
+        }
+
+        public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels,
+                boolean includeProcessStateData) {
+            mBatteryConsumersCursorWindow =
+                    new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE);
+            mBatteryConsumerDataLayout =
+                    BatteryConsumer.createBatteryConsumerDataLayout(customPowerComponentNames,
+                            includePowerModels, includeProcessStateData);
+            mBatteryConsumersCursorWindow.setNumColumns(mBatteryConsumerDataLayout.columnCount);
+
+            mCustomPowerComponentNames = customPowerComponentNames;
+            mIncludePowerModels = includePowerModels;
+            mIncludesProcessStateData = includeProcessStateData;
+            for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
+                final BatteryConsumer.BatteryConsumerData data =
+                        BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
+                                mBatteryConsumerDataLayout);
+                mAggregateBatteryConsumersBuilders[scope] =
+                        new AggregateBatteryConsumer.Builder(data, scope);
+            }
+        }
+
+        public boolean isProcessStateDataNeeded() {
+            return mIncludesProcessStateData;
+        }
+
+        /**
+         * Constructs a read-only object using the Builder values.
+         */
+        @NonNull
+        public BatteryUsageStats build() {
+            return new BatteryUsageStats(this);
+        }
+
+        /**
+         * Sets the battery capacity in milli-amp-hours.
+         */
+        public Builder setBatteryCapacity(double batteryCapacityMah) {
+            mBatteryCapacityMah = batteryCapacityMah;
+            return this;
+        }
+
+        /**
+         * Sets the timestamp of the latest battery stats reset, in milliseconds.
+         */
+        public Builder setStatsStartTimestamp(long statsStartTimestampMs) {
+            mStatsStartTimestampMs = statsStartTimestampMs;
+            return this;
+        }
+
+        /**
+         * Sets the timestamp of when the battery stats snapshot was taken, in milliseconds.
+         */
+        public Builder setStatsEndTimestamp(long statsEndTimestampMs) {
+            mStatsEndTimestampMs = statsEndTimestampMs;
+            return this;
+        }
+
+        /**
+         * Sets the duration of the stats session.  The default value of this field is
+         * statsEndTimestamp - statsStartTimestamp.
+         */
+        public Builder setStatsDuration(long statsDurationMs) {
+            mStatsDurationMs = statsDurationMs;
+            return this;
+        }
+
+        private long getStatsDuration() {
+            if (mStatsDurationMs != -1) {
+                return mStatsDurationMs;
+            } else {
+                return mStatsEndTimestampMs - mStatsStartTimestampMs;
+            }
+        }
+
+        /**
+         * Sets the battery discharge amount since BatteryStats reset as percentage of the full
+         * charge.
+         */
+        @NonNull
+        public Builder setDischargePercentage(int dischargePercentage) {
+            mDischargePercentage = dischargePercentage;
+            return this;
+        }
+
+        /**
+         * Sets the estimated battery discharge range.
+         */
+        @NonNull
+        public Builder setDischargedPowerRange(double dischargedPowerLowerBoundMah,
+                double dischargedPowerUpperBoundMah) {
+            mDischargedPowerLowerBoundMah = dischargedPowerLowerBoundMah;
+            mDischargedPowerUpperBoundMah = dischargedPowerUpperBoundMah;
+            return this;
+        }
+
+        /**
+         * Sets the total battery discharge time, in milliseconds.
+         */
+        @NonNull
+        public Builder setDischargeDurationMs(long durationMs) {
+            mDischargeDurationMs = durationMs;
+            return this;
+        }
+
+        /**
+         * Sets an approximation for how much time (in milliseconds) remains until the battery
+         * is fully discharged.
+         */
+        @NonNull
+        public Builder setBatteryTimeRemainingMs(long batteryTimeRemainingMs) {
+            mBatteryTimeRemainingMs = batteryTimeRemainingMs;
+            return this;
+        }
+
+        /**
+         * Sets an approximation for how much time (in milliseconds) remains until the battery
+         * is fully charged.
+         */
+        @NonNull
+        public Builder setChargeTimeRemainingMs(long chargeTimeRemainingMs) {
+            mChargeTimeRemainingMs = chargeTimeRemainingMs;
+            return this;
+        }
+
+        /**
+         * Sets the parceled recent history.
+         */
+        @NonNull
+        public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory) {
+            mBatteryStatsHistory = batteryStatsHistory;
+            return this;
+        }
+
+        /**
+         * Creates or returns an AggregateBatteryConsumer builder, which represents aggregate
+         * battery consumption data for the specified scope.
+         */
+        @NonNull
+        public AggregateBatteryConsumer.Builder getAggregateBatteryConsumerBuilder(
+                @AggregateBatteryConsumerScope int scope) {
+            return mAggregateBatteryConsumersBuilders[scope];
+        }
+
+        /**
+         * Creates or returns a UidBatteryConsumer, which represents battery attribution
+         * data for an individual UID.
+         */
+        @NonNull
+        public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(
+                @NonNull BatteryStats.Uid batteryStatsUid) {
+            int uid = batteryStatsUid.getUid();
+            UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
+            if (builder == null) {
+                final BatteryConsumer.BatteryConsumerData data =
+                        BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
+                                mBatteryConsumerDataLayout);
+                builder = new UidBatteryConsumer.Builder(data, batteryStatsUid);
+                mUidBatteryConsumerBuilders.put(uid, builder);
+            }
+            return builder;
+        }
+
+        /**
+         * Creates or returns a UidBatteryConsumer, which represents battery attribution
+         * data for an individual UID. This version of the method is not suitable for use
+         * with PowerCalculators.
+         */
+        @NonNull
+        public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) {
+            UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
+            if (builder == null) {
+                final BatteryConsumer.BatteryConsumerData data =
+                        BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
+                                mBatteryConsumerDataLayout);
+                builder = new UidBatteryConsumer.Builder(data, uid);
+                mUidBatteryConsumerBuilders.put(uid, builder);
+            }
+            return builder;
+        }
+
+        /**
+         * Creates or returns a UserBatteryConsumer, which represents battery attribution
+         * data for an individual {@link UserHandle}.
+         */
+        @NonNull
+        public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) {
+            UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId);
+            if (builder == null) {
+                final BatteryConsumer.BatteryConsumerData data =
+                        BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
+                                mBatteryConsumerDataLayout);
+                builder = new UserBatteryConsumer.Builder(data, userId);
+                mUserBatteryConsumerBuilders.put(userId, builder);
+            }
+            return builder;
+        }
+
+        @NonNull
+        public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
+            return mUidBatteryConsumerBuilders;
+        }
+
+        /**
+         * Adds battery usage stats from another snapshots. The two snapshots are assumed to be
+         * non-overlapping, meaning that the power consumption estimates and session durations
+         * can be simply summed across the two snapshots.  This remains true even if the timestamps
+         * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a
+         * result of realtime clock adjustments by the user or the system.
+         */
+        @NonNull
+        public Builder add(BatteryUsageStats stats) {
+            if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) {
+                throw new IllegalArgumentException(
+                        "BatteryUsageStats have different custom power components");
+            }
+
+            if (mIncludesProcessStateData && !stats.mIncludesProcessStateData) {
+                throw new IllegalArgumentException(
+                        "Added BatteryUsageStats does not include process state data");
+            }
+
+            if (mUserBatteryConsumerBuilders.size() != 0
+                    || !stats.getUserBatteryConsumers().isEmpty()) {
+                throw new UnsupportedOperationException(
+                        "Combining UserBatteryConsumers is not supported");
+            }
+
+            mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound;
+            mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound;
+            mDischargePercentage += stats.mDischargePercentage;
+            mDischargeDurationMs += stats.mDischargeDurationMs;
+
+            mStatsDurationMs = getStatsDuration() + stats.getStatsDuration();
+
+            if (mStatsStartTimestampMs == 0
+                    || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) {
+                mStatsStartTimestampMs = stats.mStatsStartTimestampMs;
+            }
+
+            final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs;
+            if (addingLaterSnapshot) {
+                mStatsEndTimestampMs = stats.mStatsEndTimestampMs;
+            }
+
+            for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
+                getAggregateBatteryConsumerBuilder(scope)
+                        .add(stats.mAggregateBatteryConsumers[scope]);
+            }
+
+            for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) {
+                getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer);
+            }
+
+            if (addingLaterSnapshot) {
+                mBatteryCapacityMah = stats.mBatteryCapacityMah;
+                mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs;
+                mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs;
+            }
+
+            return this;
+        }
+
+        /**
+         * Dumps raw contents of the cursor window for debugging.
+         */
+        void dump(PrintWriter writer) {
+            final int numRows = mBatteryConsumersCursorWindow.getNumRows();
+            int numColumns = mBatteryConsumerDataLayout.columnCount;
+            for (int i = 0; i < numRows; i++) {
+                StringBuilder sb = new StringBuilder();
+                for (int j = 0; j < numColumns; j++) {
+                    final int type = mBatteryConsumersCursorWindow.getType(i, j);
+                    switch (type) {
+                        case Cursor.FIELD_TYPE_NULL:
+                            sb.append("null, ");
+                            break;
+                        case Cursor.FIELD_TYPE_INTEGER:
+                            sb.append(mBatteryConsumersCursorWindow.getInt(i, j)).append(", ");
+                            break;
+                        case Cursor.FIELD_TYPE_FLOAT:
+                            sb.append(mBatteryConsumersCursorWindow.getFloat(i, j)).append(", ");
+                            break;
+                        case Cursor.FIELD_TYPE_STRING:
+                            sb.append(mBatteryConsumersCursorWindow.getString(i, j)).append(", ");
+                            break;
+                        case Cursor.FIELD_TYPE_BLOB:
+                            sb.append("BLOB, ");
+                            break;
+                    }
+                }
+                sb.setLength(sb.length() - 2);
+                writer.println(sb);
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/BatteryUsageStatsQuery.java b/android-34/android/os/BatteryUsageStatsQuery.java
new file mode 100644
index 0000000..b3f4d98
--- /dev/null
+++ b/android-34/android/os/BatteryUsageStatsQuery.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.util.IntArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Query parameters for the {@link BatteryStatsManager#getBatteryUsageStats()} call.
+ *
+ * @hide
+ */
+public final class BatteryUsageStatsQuery implements Parcelable {
+
+    @NonNull
+    public static final BatteryUsageStatsQuery DEFAULT =
+            new BatteryUsageStatsQuery.Builder().build();
+
+    /**
+     * Flags for the {@link BatteryStatsManager#getBatteryUsageStats()} method.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "FLAG_BATTERY_USAGE_STATS_" }, value = {
+            FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL,
+            FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY,
+            FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA,
+            FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BatteryUsageStatsFlags {}
+
+    /**
+     * Indicates that power estimations should be based on the usage time and
+     * average power constants provided in the PowerProfile, even if on-device power monitoring
+     * is available.
+     *
+     * @hide
+     */
+    public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 0x0001;
+
+    /**
+     * Indicates that battery history should be included in the BatteryUsageStats.
+     * @hide
+     */
+    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 0x0002;
+
+    /**
+     * Indicates that identifiers of power models used for computations of power
+     * consumption should be included in the BatteryUsageStats.
+     */
+    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS = 0x0004;
+
+    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA = 0x0008;
+
+    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS = 0x0010;
+
+    private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000;
+
+    private final int mFlags;
+    @NonNull
+    private final int[] mUserIds;
+    private final long mMaxStatsAgeMs;
+    private final long mFromTimestamp;
+    private final long mToTimestamp;
+    private final @BatteryConsumer.PowerComponent int[] mPowerComponents;
+
+    private BatteryUsageStatsQuery(@NonNull Builder builder) {
+        mFlags = builder.mFlags;
+        mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
+                : new int[]{UserHandle.USER_ALL};
+        mMaxStatsAgeMs = builder.mMaxStatsAgeMs;
+        mFromTimestamp = builder.mFromTimestamp;
+        mToTimestamp = builder.mToTimestamp;
+        mPowerComponents = builder.mPowerComponents;
+    }
+
+    @BatteryUsageStatsFlags
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Returns an array of users for which the attribution is requested.  It may
+     * contain {@link UserHandle#USER_ALL} to indicate that the attribution
+     * should be performed for all users. Battery consumed by users <b>not</b> included
+     * in this array will be returned in the aggregated form as {@link UserBatteryConsumer}'s.
+     */
+    @NonNull
+    public int[] getUserIds() {
+        return mUserIds;
+    }
+
+    /**
+     * Returns true if the power calculations must be based on the PowerProfile constants,
+     * even if measured energy data is available.
+     */
+    public boolean shouldForceUsePowerProfileModel() {
+        return (mFlags & FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0;
+    }
+
+    public boolean isProcessStateDataNeeded() {
+        return (mFlags & FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0;
+    }
+
+    /**
+     * Returns the power components that should be estimated or null if all power components
+     * are being requested.
+     */
+    public int[] getPowerComponents() {
+        return mPowerComponents;
+    }
+
+    /**
+     * Returns the client's tolerance for stale battery stats. The data is allowed to be up to
+     * this many milliseconds out-of-date.
+     */
+    public long getMaxStatsAge() {
+        return mMaxStatsAgeMs;
+    }
+
+    /**
+     * Returns the exclusive lower bound of the stored snapshot timestamps that should be included
+     * in the aggregation.  Ignored if {@link #getToTimestamp()} is zero.
+     */
+    public long getFromTimestamp() {
+        return mFromTimestamp;
+    }
+
+    /**
+     * Returns the inclusive upper bound of the stored snapshot timestamps that should
+     * be included in the aggregation.  The default is to include only the current stats
+     * accumulated since the latest battery reset.
+     */
+    public long getToTimestamp() {
+        return mToTimestamp;
+    }
+
+    private BatteryUsageStatsQuery(Parcel in) {
+        mFlags = in.readInt();
+        mUserIds = new int[in.readInt()];
+        in.readIntArray(mUserIds);
+        mMaxStatsAgeMs = in.readLong();
+        mFromTimestamp = in.readLong();
+        mToTimestamp = in.readLong();
+        mPowerComponents = in.createIntArray();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mFlags);
+        dest.writeInt(mUserIds.length);
+        dest.writeIntArray(mUserIds);
+        dest.writeLong(mMaxStatsAgeMs);
+        dest.writeLong(mFromTimestamp);
+        dest.writeLong(mToTimestamp);
+        dest.writeIntArray(mPowerComponents);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<BatteryUsageStatsQuery> CREATOR =
+            new Creator<BatteryUsageStatsQuery>() {
+                @Override
+                public BatteryUsageStatsQuery createFromParcel(Parcel in) {
+                    return new BatteryUsageStatsQuery(in);
+                }
+
+                @Override
+                public BatteryUsageStatsQuery[] newArray(int size) {
+                    return new BatteryUsageStatsQuery[size];
+                }
+            };
+
+    /**
+     * Builder for BatteryUsageStatsQuery.
+     */
+    public static final class Builder {
+        private int mFlags;
+        private IntArray mUserIds;
+        private long mMaxStatsAgeMs = DEFAULT_MAX_STATS_AGE_MS;
+        private long mFromTimestamp;
+        private long mToTimestamp;
+        private @BatteryConsumer.PowerComponent int[] mPowerComponents;
+
+        /**
+         * Builds a read-only BatteryUsageStatsQuery object.
+         */
+        public BatteryUsageStatsQuery build() {
+            return new BatteryUsageStatsQuery(this);
+        }
+
+        /**
+         * Add a user whose battery stats should be included in the battery usage stats.
+         * {@link UserHandle#USER_ALL} will be used by default if no users are added explicitly.
+         */
+        public Builder addUser(@NonNull UserHandle userHandle) {
+            if (mUserIds == null) {
+                mUserIds = new IntArray(1);
+            }
+            mUserIds.add(userHandle.getIdentifier());
+            return this;
+        }
+
+        /**
+         * Requests that battery history be included in the BatteryUsageStats.
+         */
+        public Builder includeBatteryHistory() {
+            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY;
+            return this;
+        }
+
+        /**
+         * Requests that per-process state data be included in the BatteryUsageStats, if
+         * available. Check {@link BatteryUsageStats#isProcessStateDataIncluded()} on the result
+         * to see if the data is available.
+         */
+        public Builder includeProcessStateData() {
+            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA;
+            return this;
+        }
+
+        /**
+         * Requests to return modeled battery usage stats only, even if on-device
+         * power monitoring data is available.
+         *
+         * Should only be used for testing and debugging.
+         */
+        public Builder powerProfileModeledOnly() {
+            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL;
+            return this;
+        }
+
+        /**
+         * Requests to return identifiers of models that were used for estimation
+         * of power consumption.
+         *
+         * Should only be used for testing and debugging.
+         */
+        public Builder includePowerModels() {
+            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS;
+            return this;
+        }
+
+        /**
+         * Requests to return only statistics for the specified power components.  The default
+         * is all power components.
+         */
+        public Builder includePowerComponents(
+                @BatteryConsumer.PowerComponent int[] powerComponents) {
+            mPowerComponents = powerComponents;
+            return this;
+        }
+
+        /**
+         * Requests to return attribution data for virtual UIDs such as
+         * {@link Process#SDK_SANDBOX_VIRTUAL_UID}.
+         */
+        public Builder includeVirtualUids() {
+            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS;
+            return this;
+        }
+
+        /**
+         * Requests to aggregate stored snapshots between the two supplied timestamps
+         * @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis()
+         * @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis()
+         */
+        public Builder aggregateSnapshots(long fromTimestamp, long toTimestamp) {
+            mFromTimestamp = fromTimestamp;
+            mToTimestamp = toTimestamp;
+            return this;
+        }
+
+        /**
+         * Set the client's tolerance for stale battery stats. The data may be up to
+         * this many milliseconds out-of-date.
+         */
+        public Builder setMaxStatsAgeMs(long maxStatsAgeMs) {
+            mMaxStatsAgeMs = maxStatsAgeMs;
+            return this;
+        }
+    }
+}
diff --git a/android-34/android/os/BestClock.java b/android-34/android/os/BestClock.java
new file mode 100644
index 0000000..aa066b6
--- /dev/null
+++ b/android-34/android/os/BestClock.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.ZoneId;
+import java.util.Arrays;
+
+/**
+ * Single {@link Clock} that will return the best available time from a set of
+ * prioritized {@link Clock} instances.
+ * <p>
+ * For example, when {@link SystemClock#currentNetworkTimeClock()} isn't able to
+ * provide the time, this class could use {@link Clock#systemUTC()} instead.
+ *
+ * @hide
+ */
+public class BestClock extends SimpleClock {
+    private static final String TAG = "BestClock";
+
+    private final Clock[] clocks;
+
+    public BestClock(ZoneId zone, Clock... clocks) {
+        super(zone);
+        this.clocks = clocks;
+    }
+
+    @Override
+    public long millis() {
+        for (Clock clock : clocks) {
+            try {
+                return clock.millis();
+            } catch (DateTimeException e) {
+                // Ignore and attempt the next clock
+                Log.w(TAG, e.toString());
+            }
+        }
+        throw new DateTimeException(
+                "No clocks in " + Arrays.toString(clocks) + " were able to provide time");
+    }
+}
diff --git a/android-34/android/os/Binder.java b/android-34/android/os/Binder.java
new file mode 100644
index 0000000..00676f3
--- /dev/null
+++ b/android-34/android/os/Binder.java
@@ -0,0 +1,1418 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.AppOpsManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.ExceptionUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BinderCallHeavyHitterWatcher;
+import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
+import com.android.internal.os.BinderInternal;
+import com.android.internal.os.BinderInternal.CallSession;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.io.IoUtils;
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+/**
+ * Base class for a remotable object, the core part of a lightweight
+ * remote procedure call mechanism defined by {@link IBinder}.
+ * This class is an implementation of IBinder that provides
+ * standard local implementation of such an object.
+ *
+ * <p>Most developers will not implement this class directly, instead using the
+ * <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired
+ * interface, having it generate the appropriate Binder subclass. You can,
+ * however, derive directly from Binder to implement your own custom RPC
+ * protocol or simply instantiate a raw Binder object directly to use as a
+ * token that can be shared across processes.
+ *
+ * <p>This class is just a basic IPC primitive; it has no impact on an application's
+ * lifecycle, and is valid only as long as the process that created it continues to run.
+ * To use this correctly, you must be doing so within the context of a top-level
+ * application component (a {@link android.app.Service}, {@link android.app.Activity},
+ * or {@link android.content.ContentProvider}) that lets the system know your process
+ * should remain running.
+ *
+ * <p>You must keep in mind the situations in which your process
+ * could go away, and thus require that you later re-create a new Binder and re-attach
+ * it when the process starts again. For example, if you are using this within an
+ * {@link android.app.Activity}, your activity's process may be killed any time the
+ * activity is not started; if the activity is later re-created you will need to
+ * create a new Binder and hand it back to the correct place again; you need to be
+ * aware that your process may be started for another reason (for example to receive
+ * a broadcast) that will not involve re-creating the activity and thus run its code
+ * to create a new Binder.
+ *
+ * @see IBinder
+ */
+public class Binder implements IBinder {
+    /*
+     * Set this flag to true to detect anonymous, local or member classes
+     * that extend this Binder class and that are not static. These kind
+     * of classes can potentially create leaks.
+     */
+    private static final boolean FIND_POTENTIAL_LEAKS = false;
+    /** @hide */
+    public static final boolean CHECK_PARCEL_SIZE = false;
+    static final String TAG = "Binder";
+
+    /** @hide */
+    public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE
+
+    /**
+     * Value to represents that a calling work source is not set.
+     *
+     * <p>This constant needs to be kept in sync with IPCThreadState::kUnsetWorkSource.
+     *
+     * @hide
+     */
+    public static final int UNSET_WORKSOURCE = -1;
+
+    /**
+     * Control whether {@link #dump(FileDescriptor, PrintWriter, String[]) dump()}
+     * calls are allowed.
+     */
+    private static volatile String sDumpDisabled = null;
+
+    /**
+     * Global transaction tracker instance for this process.
+     */
+    private static volatile TransactionTracker sTransactionTracker = null;
+
+    /**
+     * Global observer for this process.
+     */
+    private static BinderInternal.Observer sObserver = null;
+
+    /**
+     * Guestimate of native memory associated with a Binder.
+     */
+    private static final int NATIVE_ALLOCATION_SIZE = 500;
+
+    private static native long getNativeFinalizer();
+
+    // Use a Holder to allow static initialization of Binder in the boot image, and
+    // possibly to avoid some initialization ordering issues.
+    private static class NoImagePreloadHolder {
+        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                Binder.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+    }
+
+    /**
+     * The watcher to monitor the heavy hitter from incoming transactions
+     */
+    private static volatile BinderCallHeavyHitterWatcher sHeavyHitterWatcher = null;
+
+    // Transaction tracking code.
+
+    /**
+     * Flag indicating whether we should be tracing transact calls.
+     */
+    private static volatile boolean sStackTrackingEnabled = false;
+
+    /**
+     * Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
+     * {@link TransactionTracker}.
+     *
+     * @hide
+     */
+    public static void enableStackTracking() {
+        sStackTrackingEnabled = true;
+    }
+
+    /**
+     * Disable Binder IPC stack tracking.
+     *
+     * @hide
+     */
+    public static void disableStackTracking() {
+        sStackTrackingEnabled = false;
+    }
+
+    /**
+     * Check if binder transaction stack tracking is enabled.
+     *
+     * @hide
+     */
+    public static boolean isStackTrackingEnabled() {
+        return sStackTrackingEnabled;
+    }
+
+    /**
+     * Get the binder transaction tracker for this process.
+     *
+     * @hide
+     */
+    public synchronized static TransactionTracker getTransactionTracker() {
+        if (sTransactionTracker == null)
+            sTransactionTracker = new TransactionTracker();
+        return sTransactionTracker;
+    }
+
+    /**
+     * Get the binder transaction observer for this process.
+     *
+     * @hide
+     */
+    public static void setObserver(@Nullable BinderInternal.Observer observer) {
+        sObserver = observer;
+    }
+
+    /** @hide */
+    static volatile boolean sWarnOnBlocking = false;
+
+    /**
+     * Warn if any blocking binder transactions are made out from this process.
+     * This is typically only useful for the system process, to prevent it from
+     * blocking on calls to external untrusted code. Instead, all outgoing calls
+     * that require a result must be sent as {@link IBinder#FLAG_ONEWAY} calls
+     * which deliver results through a callback interface.
+     *
+     * @hide
+     */
+    public static void setWarnOnBlocking(boolean warnOnBlocking) {
+        sWarnOnBlocking = warnOnBlocking;
+    }
+
+    /**
+     * Allow blocking calls on the given interface, overriding the requested
+     * value of {@link #setWarnOnBlocking(boolean)}.
+     *
+     * <p>This should only be rarely called when you are <em>absolutely sure</em>
+     * the remote interface is a built-in system component that can never be
+     * upgraded. In particular, this <em>must never</em> be called for
+     * interfaces hosted by package that could be upgraded or replaced,
+     * otherwise you risk system instability if that remote interface wedges.
+     *
+     * @hide
+     */
+    public static IBinder allowBlocking(IBinder binder) {
+        try {
+            if (binder instanceof BinderProxy) {
+                ((BinderProxy) binder).mWarnOnBlocking = false;
+            } else if (binder != null && binder.getInterfaceDescriptor() != null
+                    && binder.queryLocalInterface(binder.getInterfaceDescriptor()) == null) {
+                Log.w(TAG, "Unable to allow blocking on interface " + binder);
+            }
+        } catch (RemoteException ignored) {
+        }
+        return binder;
+    }
+
+    /**
+     * Reset the given interface back to the default blocking behavior,
+     * reverting any changes made by {@link #allowBlocking(IBinder)}.
+     *
+     * @hide
+     */
+    public static IBinder defaultBlocking(IBinder binder) {
+        if (binder instanceof BinderProxy) {
+            ((BinderProxy) binder).mWarnOnBlocking = sWarnOnBlocking;
+        }
+        return binder;
+    }
+
+    /**
+     * Inherit the current {@link #allowBlocking(IBinder)} value from one given
+     * interface to another.
+     *
+     * @hide
+     */
+    public static void copyAllowBlocking(IBinder fromBinder, IBinder toBinder) {
+        if (fromBinder instanceof BinderProxy && toBinder instanceof BinderProxy) {
+            ((BinderProxy) toBinder).mWarnOnBlocking = ((BinderProxy) fromBinder).mWarnOnBlocking;
+        }
+    }
+
+    static ThreadLocal<Boolean> sWarnOnBlockingOnCurrentThread =
+            ThreadLocal.withInitial(() -> sWarnOnBlocking);
+
+    /**
+     * Allow blocking calls for the current thread.
+     *
+     * @see {@link #allowBlocking}.
+     *
+     * @hide
+     */
+    public static void allowBlockingForCurrentThread() {
+        sWarnOnBlockingOnCurrentThread.set(false);
+    }
+
+    /**
+     * Reset the current thread to the default blocking behavior.
+     *
+     * @see {@link #defaultBlocking}.
+     *
+     * @hide
+     */
+    public static void defaultBlockingForCurrentThread() {
+        sWarnOnBlockingOnCurrentThread.set(sWarnOnBlocking);
+    }
+
+    /**
+     * Raw native pointer to JavaBBinderHolder object. Owned by this Java object. Not null.
+     */
+    @UnsupportedAppUsage
+    private final long mObject;
+
+    private IInterface mOwner;
+    @Nullable
+    private String mDescriptor;
+    private volatile AtomicReferenceArray<String> mTransactionTraceNames = null;
+    private volatile String mSimpleDescriptor = null;
+    private static final int TRANSACTION_TRACE_NAME_ID_LIMIT = 1024;
+
+    /**
+     * Return the ID of the process that sent you the current transaction
+     * that is being processed. This PID can be used with higher-level
+     * system services to determine its identity and check permissions.
+     * If the current thread is not currently executing an incoming transaction,
+     * then its own PID is returned.
+     *
+     * Warning: oneway transactions do not receive PID.
+     */
+    @CriticalNative
+    public static final native int getCallingPid();
+
+    /**
+     * Return the Linux UID assigned to the process that sent you the
+     * current transaction that is being processed. This UID can be used with
+     * higher-level system services to determine its identity and check
+     * permissions. If the current thread is not currently executing an
+     * incoming transaction, then its own UID is returned.
+     */
+    @CriticalNative
+    public static final native int getCallingUid();
+
+    /**
+     * Returns {@code true} if the current thread is currently executing an
+     * incoming transaction.
+     *
+     * @hide
+     */
+    @CriticalNative
+    public static final native boolean isDirectlyHandlingTransaction();
+
+    /**
+    * Returns {@code true} if the current thread has had its identity
+    * set explicitly via {@link #clearCallingIdentity()}
+    *
+    * @hide
+    */
+    @CriticalNative
+    private static native boolean hasExplicitIdentity();
+
+    /**
+     * Return the Linux UID assigned to the process that sent the transaction
+     * currently being processed.
+     *
+     * @throws IllegalStateException if the current thread is not currently
+     * executing an incoming transaction and the calling identity has not been
+     * explicitly set with {@link #clearCallingIdentity()}
+     */
+    public static final int getCallingUidOrThrow() {
+        if (!isDirectlyHandlingTransaction() && !hasExplicitIdentity()) {
+            throw new IllegalStateException(
+                  "Thread is not in a binder transaction, "
+                  + "and the calling identity has not been "
+                  + "explicitly set with clearCallingIdentity");
+        }
+        return getCallingUid();
+    }
+
+    /**
+     * Return the Linux UID assigned to the process that sent the transaction
+     * currently being processed.
+     *
+     * Slog.wtf if the current thread is not currently
+     * executing an incoming transaction and the calling identity has not been
+     * explicitly set with {@link #clearCallingIdentity()}
+     *
+     * @hide
+     */
+    public static final int getCallingUidOrWtf(String message) {
+        if (!isDirectlyHandlingTransaction() && !hasExplicitIdentity()) {
+            Slog.wtf(TAG,
+                    message + ": Thread is not in a binder transaction, "
+                            + "and the calling identity has not been "
+                            + "explicitly set with clearCallingIdentity");
+        }
+        return getCallingUid();
+    }
+
+    /**
+     * Return the UserHandle assigned to the process that sent you the
+     * current transaction that is being processed. This is the user
+     * of the caller. It is distinct from {@link #getCallingUid()} in that a
+     * particular user will have multiple distinct apps running under it each
+     * with their own UID. If the current thread is not currently executing an
+     * incoming transaction, then its own UserHandle is returned.
+     *
+     * @see UserHandle
+     */
+    public static final @NonNull UserHandle getCallingUserHandle() {
+        return UserHandle.of(UserHandle.getUserId(getCallingUid()));
+    }
+
+    /**
+     * Reset the identity of the incoming IPC on the current thread. This can
+     * be useful if, while handling an incoming call, you will be calling
+     * on interfaces of other objects that may be local to your process and
+     * need to do permission checks on the calls coming into them (so they
+     * will check the permission of your own local process, and not whatever
+     * process originally called you).
+     *
+     * @return Returns an opaque token that can be used to restore the
+     * original calling identity by passing it to
+     * {@link #restoreCallingIdentity(long)}.
+     *
+     * @see #getCallingPid()
+     * @see #getCallingUid()
+     * @see #restoreCallingIdentity(long)
+     */
+    @CriticalNative
+    public static final native long clearCallingIdentity();
+
+    /**
+     * Restore the identity of the incoming IPC on the current thread
+     * back to a previously identity that was returned by {@link
+     * #clearCallingIdentity}.
+     *
+     * @param token The opaque token that was previously returned by
+     * {@link #clearCallingIdentity}.
+     *
+     * @see #clearCallingIdentity
+     */
+    @CriticalNative
+    public static final native void restoreCallingIdentity(long token);
+
+    /**
+     * Convenience method for running the provided action enclosed in
+     * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}.
+     *
+     * <p>Any exception thrown by the given action will be caught and
+     * rethrown after the call to {@link #restoreCallingIdentity}.
+     *
+     * @hide
+     */
+    public static final void withCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+        Throwable throwableToPropagate = null;
+        final long callingIdentity = clearCallingIdentity();
+        try {
+            action.runOrThrow();
+        } catch (Throwable throwable) {
+            throwableToPropagate = throwable;
+        } finally {
+            restoreCallingIdentity(callingIdentity);
+            if (throwableToPropagate != null) {
+                throw ExceptionUtils.propagate(throwableToPropagate);
+            }
+        }
+    }
+
+    /**
+     * Convenience method for running the provided action enclosed in
+     * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result.
+     *
+     * <p>Any exception thrown by the given action will be caught and rethrown after
+     * the call to {@link #restoreCallingIdentity}.
+     *
+     * @hide
+     */
+    public static final <T> T withCleanCallingIdentity(@NonNull ThrowingSupplier<T> action) {
+        Throwable throwableToPropagate = null;
+        final long callingIdentity = clearCallingIdentity();
+        try {
+            return action.getOrThrow();
+        } catch (Throwable throwable) {
+            throwableToPropagate = throwable;
+            return null; // overridden by throwing in finally block
+        } finally {
+            restoreCallingIdentity(callingIdentity);
+            if (throwableToPropagate != null) {
+                throw ExceptionUtils.propagate(throwableToPropagate);
+            }
+        }
+    }
+
+    /**
+     * Sets the native thread-local StrictMode policy mask.
+     *
+     * <p>The StrictMode settings are kept in two places: a Java-level
+     * threadlocal for libcore/Dalvik, and a native threadlocal (set
+     * here) for propagation via Binder calls. This is a little
+     * unfortunate, but necessary to break otherwise more unfortunate
+     * dependencies either of Dalvik on Android, or Android
+     * native-only code on Dalvik.
+     *
+     * @see StrictMode
+     *
+     * @hide
+     */
+    @CriticalNative
+    public static final native void setThreadStrictModePolicy(int policyMask);
+
+    /**
+     * Gets the current native thread-local StrictMode policy mask.
+     *
+     * @see #setThreadStrictModePolicy
+     *
+     * @hide
+     */
+    @CriticalNative
+    public static final native int getThreadStrictModePolicy();
+
+    /**
+     * Sets the work source for this thread.
+     *
+     * <p>All the following binder calls on this thread will use the provided work source. If this
+     * is called during an on-going binder transaction, all the following binder calls will use the
+     * work source until the end of the transaction.
+     *
+     * <p>The concept of worksource is similar to {@link WorkSource}. However, for performance
+     * reasons, we only support one UID. This UID represents the original user responsible for the
+     * binder calls.
+     *
+     * <p>{@link #restoreCallingWorkSource(long)} must always be called after setting the
+     * worksource.
+     *
+     * <p>A typical use case would be
+     * <pre>
+     * long token = Binder.setCallingWorkSourceUid(uid);
+     * try {
+     *   // Call an API.
+     * } finally {
+     *   Binder.restoreCallingWorkSource(token);
+     * }
+     * </pre>
+     *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
+     *
+     * @param workSource The original UID responsible for the binder call.
+     * @return token to restore original work source.
+     */
+    @CriticalNative
+    public static final native long setCallingWorkSourceUid(int workSource);
+
+    /**
+     * Returns the work source set by the caller.
+     *
+     * <p>Unlike {@link #getCallingUid()}, this result of this method cannot be trusted. The
+     * caller can set the value to whatever they want. Only use this value if you trust the calling
+     * UID.
+     *
+     * @return The original UID responsible for the binder transaction.
+     */
+    @CriticalNative
+    public static final native int getCallingWorkSourceUid();
+
+    /**
+     * Clears the work source on this thread.
+     *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
+     *
+     * <p>{@link #restoreCallingWorkSource(long)} must always be called after clearing the
+     * worksource.
+     *
+     * <p>A typical use case would be
+     * <pre>
+     * long token = Binder.clearCallingWorkSource();
+     * try {
+     *   // Call an API.
+     * } finally {
+     *   Binder.restoreCallingWorkSource(token);
+     * }
+     * </pre>
+     *
+     * @return token to restore original work source.
+     */
+    @CriticalNative
+    public static final native long clearCallingWorkSource();
+
+    /**
+     * Restores the work source on this thread using a token returned by
+     * {@link #setCallingWorkSourceUid(int)} or {@link #clearCallingWorkSource()}.
+     *
+     * <p>A typical use case would be
+     * <pre>
+     * long token = Binder.setCallingWorkSourceUid(uid);
+     * try {
+     *   // Call an API.
+     * } finally {
+     *   Binder.restoreCallingWorkSource(token);
+     * }
+     * </pre>
+     */
+    @CriticalNative
+    public static final native void restoreCallingWorkSource(long token);
+
+    /**
+     * Mark as being built with VINTF-level stability promise. This API should
+     * only ever be invoked by generated code from the aidl compiler. It means
+     * that the interface represented by this binder is guaranteed to be kept
+     * stable for several years, according to the VINTF compatibility lifecycle,
+     * and the build system also keeps snapshots of these APIs and invokes the
+     * AIDL compiler to make sure that these snapshots are backwards compatible.
+     * Instead of using this API, use the @VintfStability annotation on your
+     * AIDL interface.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    public final native void markVintfStability();
+
+    /**
+     * Use a VINTF-stability binder w/o VINTF requirements. Should be called
+     * on a binder before it is sent out of process.
+     *
+     * <p>This must be called before the object is sent to another process.
+     *
+     * @hide
+     */
+    public final native void forceDowngradeToSystemStability();
+
+    /**
+     * Flush any Binder commands pending in the current thread to the kernel
+     * driver. This can be
+     * useful to call before performing an operation that may block for a long
+     * time, to ensure that any pending object references have been released
+     * in order to prevent the process from holding on to objects longer than
+     * it needs to.
+     */
+    public static final native void flushPendingCommands();
+
+    /**
+     * Add the calling thread to the IPC thread pool. This function does
+     * not return until the current process is exiting.
+     */
+    public static final void joinThreadPool() {
+        BinderInternal.joinThreadPool();
+    }
+
+    /**
+     * Returns true if the specified interface is a proxy.
+     *
+     * @hide
+     */
+    public static final boolean isProxy(IInterface iface) {
+        return iface.asBinder() != iface;
+    }
+
+    /**
+     * Call blocks until the number of executing binder threads is less
+     * than the maximum number of binder threads allowed for this process.
+     *
+     * @hide
+     */
+    public static final native void blockUntilThreadAvailable();
+
+    /**
+     * Default constructor just initializes the object.
+     *
+     * <p>If you're creating a Binder token (a Binder object without an attached interface),
+     * you should use {@link #Binder(String)} instead.
+     */
+    public Binder() {
+        this(null);
+    }
+
+    /**
+     * Constructor for creating a raw Binder object (token) along with a descriptor.
+     *
+     * <p>The descriptor of binder objects usually specifies the interface they are implementing.
+     * In case of binder tokens, no interface is implemented, and the descriptor can be used
+     * as a sort of tag to help identify the binder token. This will help identify remote
+     * references to these objects more easily when debugging.
+     *
+     * @param descriptor Used to identify the creator of this token, for example the class name.
+     * Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
+     * help identify them.
+     */
+    public Binder(@Nullable String descriptor) {
+        mObject = getNativeBBinderHolder();
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
+
+        if (FIND_POTENTIAL_LEAKS) {
+            final Class<? extends Binder> klass = getClass();
+            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
+                    (klass.getModifiers() & Modifier.STATIC) == 0) {
+                Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
+                    klass.getCanonicalName());
+            }
+        }
+        mDescriptor = descriptor;
+    }
+
+    /**
+     * Convenience method for associating a specific interface with the Binder.
+     * After calling, {@link #queryLocalInterface(String) queryLocalInterface()}
+     * will be implemented for you to return the given owner IInterface when
+     * the corresponding descriptor is requested.
+     */
+    public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
+        mOwner = owner;
+        mDescriptor = descriptor;
+    }
+
+    /**
+     * Default implementation returns an empty interface name.
+     */
+    public @Nullable String getInterfaceDescriptor() {
+        return mDescriptor;
+    }
+
+    /**
+     * Default implementation always returns true -- if you got here,
+     * the object is alive.
+     */
+    public boolean pingBinder() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Note that if you're calling on a local binder, this always returns true
+     * because your process is alive if you're calling it.
+     */
+    public boolean isBinderAlive() {
+        return true;
+    }
+
+    /**
+     * Use information supplied to {@link #attachInterface attachInterface()}
+     * to return the associated {@link IInterface} if it matches the requested
+     * descriptor.
+     */
+    public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
+        if (mDescriptor != null && mDescriptor.equals(descriptor)) {
+            return mOwner;
+        }
+        return null;
+    }
+
+    /**
+     * Control disabling of dump calls in this process. This is used by the system
+     * process watchdog to disable incoming dump calls while it has detecting the system
+     * is hung and is reporting that back to the activity controller. This is to
+     * prevent the controller from getting hung up on bug reports at this point.
+     *
+     * @param msg The message to show instead of the dump; if null, dumps are
+     * re-enabled.
+     *
+     * @hide
+     */
+    public static void setDumpDisabled(String msg) {
+        sDumpDisabled = msg;
+    }
+
+    /**
+     * Listener to be notified about each proxy-side binder call.
+     *
+     * @see {@link #setProxyTransactListener}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface ProxyTransactListener {
+        /**
+         * Called before onTransact.
+         *
+         * @return an object that will be passed back to {@link #onTransactEnded} (or null).,
+         *
+         * @hide
+         */
+        @Nullable
+        default Object onTransactStarted(@NonNull IBinder binder, int transactionCode, int flags) {
+            return onTransactStarted(binder, transactionCode);
+        }
+
+        /**
+         * Called before onTransact.
+         *
+         * @return an object that will be passed back to {@link #onTransactEnded} (or null).
+         */
+        @Nullable
+        Object onTransactStarted(@NonNull IBinder binder, int transactionCode);
+
+        /**
+         * Called after onTransact (even when an exception is thrown).
+         *
+         * @param session The object return by {@link #onTransactStarted}.
+         */
+        void onTransactEnded(@Nullable Object session);
+    }
+
+    /**
+     * Propagates the work source to binder calls executed by the system server.
+     *
+     * <li>By default, this listener will propagate the worksource if the outgoing call happens on
+     * the same thread as the incoming binder call.
+     * <li>Custom attribution can be done by calling {@link ThreadLocalWorkSource#setUid(int)}.
+     *
+     * @hide
+     */
+    public static class PropagateWorkSourceTransactListener implements ProxyTransactListener {
+        @Override
+        public Object onTransactStarted(IBinder binder, int transactionCode) {
+            // Note that {@link #getCallingUid()} is already set to the UID of the current
+            // process when this method is called.
+            //
+            // We use {@link ThreadLocalWorkSource} instead. It also allows feature owners to set
+            // {@link ThreadLocalWorkSource#set(int)} manually to attribute resources to a UID.
+            int uid = ThreadLocalWorkSource.getUid();
+            if (uid != ThreadLocalWorkSource.UID_NONE) {
+                return Binder.setCallingWorkSourceUid(uid);
+            }
+            return null;
+        }
+
+        @Override
+        public void onTransactEnded(Object session) {
+            if (session != null) {
+                long token = (long) session;
+                Binder.restoreCallingWorkSource(token);
+            }
+        }
+    }
+
+    /**
+     * Sets a listener for the transact method on the proxy-side.
+     *
+     * <li>The listener is global. Only fast operations should be done to avoid thread
+     * contentions.
+     * <li>The listener implementation needs to handle synchronization if needed. The methods on the
+     * listener can be called concurrently.
+     * <li>Listener set will be used for new transactions. On-going transaction will still use the
+     * previous listener (if already set).
+     * <li>The listener is called on the critical path of the binder transaction so be careful about
+     * performance.
+     * <li>Never execute another binder transaction inside the listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void setProxyTransactListener(@Nullable ProxyTransactListener listener) {
+        BinderProxy.setTransactListener(listener);
+    }
+
+    /**
+     * Default implementation is a stub that returns false. You will want
+     * to override this to do the appropriate unmarshalling of transactions.
+     *
+     * <p>If you want to call this, call transact().
+     *
+     * <p>Implementations that are returning a result should generally use
+     * {@link Parcel#writeNoException() Parcel.writeNoException} and
+     * {@link Parcel#writeException(Exception) Parcel.writeException} to propagate
+     * exceptions back to the caller.
+     *
+     * @param code The action to perform. This should be a number between
+     * {@link #FIRST_CALL_TRANSACTION} and {@link #LAST_CALL_TRANSACTION}.
+     * @param data Marshalled data being received from the caller.
+     * @param reply If the caller is expecting a result back, it should be marshalled
+     * in to here.
+     * @param flags Additional operation flags. Either 0 for a normal
+     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+     *
+     * @return Return true on a successful call; returning false is generally used to
+     * indicate that you did not understand the transaction code.
+     */
+    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
+            int flags) throws RemoteException {
+        if (code == INTERFACE_TRANSACTION) {
+            reply.writeString(getInterfaceDescriptor());
+            return true;
+        } else if (code == DUMP_TRANSACTION) {
+            ParcelFileDescriptor fd = data.readFileDescriptor();
+            String[] args = data.readStringArray();
+            if (fd != null) {
+                try {
+                    dump(fd.getFileDescriptor(), args);
+                } finally {
+                    IoUtils.closeQuietly(fd);
+                }
+            }
+            // Write the StrictMode header.
+            if (reply != null) {
+                reply.writeNoException();
+            } else {
+                StrictMode.clearGatheredViolations();
+            }
+            return true;
+        } else if (code == SHELL_COMMAND_TRANSACTION) {
+            ParcelFileDescriptor in = data.readFileDescriptor();
+            ParcelFileDescriptor out = data.readFileDescriptor();
+            ParcelFileDescriptor err = data.readFileDescriptor();
+            String[] args = data.readStringArray();
+            ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
+            ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
+            try {
+                if (out != null) {
+                    shellCommand(in != null ? in.getFileDescriptor() : null,
+                            out.getFileDescriptor(),
+                            err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
+                            args, shellCallback, resultReceiver);
+                }
+            } finally {
+                IoUtils.closeQuietly(in);
+                IoUtils.closeQuietly(out);
+                IoUtils.closeQuietly(err);
+                // Write the StrictMode header.
+                if (reply != null) {
+                    reply.writeNoException();
+                } else {
+                    StrictMode.clearGatheredViolations();
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Resolves a transaction code to a human readable name.
+     *
+     * <p>Default implementation is a stub that returns null.
+     *
+     * <p>AIDL generated code will return the original method name.
+     *
+     * @param transactionCode The code to resolve.
+     * @return A human readable name.
+     *
+     * @hide
+     */
+    public @Nullable String getTransactionName(int transactionCode) {
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public final @NonNull String getTransactionTraceName(int transactionCode) {
+        if (mTransactionTraceNames == null) {
+            final int highestId = Math.min(getMaxTransactionId(), TRANSACTION_TRACE_NAME_ID_LIMIT);
+            mSimpleDescriptor = getSimpleDescriptor();
+            mTransactionTraceNames = new AtomicReferenceArray(highestId + 1);
+        }
+
+        final int index = transactionCode - FIRST_CALL_TRANSACTION;
+        if (index < 0 || index >= mTransactionTraceNames.length()) {
+            return mSimpleDescriptor + "#" + transactionCode;
+        }
+
+        String transactionTraceName = mTransactionTraceNames.getAcquire(index);
+        if (transactionTraceName == null) {
+            final String transactionName = getTransactionName(transactionCode);
+            final StringBuffer buf = new StringBuffer();
+
+            // Keep trace name consistent with cpp trace name in:
+            // system/tools/aidl/generate_cpp.cpp
+            buf.append("AIDL::java::");
+            if (transactionName != null) {
+                buf.append(mSimpleDescriptor).append("::").append(transactionName);
+            } else {
+                buf.append(mSimpleDescriptor).append("::#").append(transactionCode);
+            }
+            buf.append("::server");
+
+            transactionTraceName = buf.toString();
+            mTransactionTraceNames.setRelease(index, transactionTraceName);
+        }
+
+        return transactionTraceName;
+    }
+
+    private @NonNull String getSimpleDescriptor() {
+        String descriptor = mDescriptor;
+        if (descriptor == null) {
+            // Just "Binder" to avoid null checks in transaction name tracing.
+            return "Binder";
+        }
+
+        final int dot = descriptor.lastIndexOf(".");
+        if (dot > 0) {
+            // Strip the package name
+            return descriptor.substring(dot + 1);
+        }
+        return descriptor;
+    }
+
+    /**
+     * @return The highest user-defined transaction id of all transactions.
+     * @hide
+     */
+    public int getMaxTransactionId() {
+        return 0;
+    }
+
+    /**
+     * Implemented to call the more convenient version
+     * {@link #dump(FileDescriptor, PrintWriter, String[])}.
+     */
+    public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) {
+        FileOutputStream fout = new FileOutputStream(fd);
+        PrintWriter pw = new FastPrintWriter(fout);
+        try {
+            doDump(fd, pw, args);
+        } finally {
+            pw.flush();
+        }
+    }
+
+    void doDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final String disabled = sDumpDisabled;
+        if (disabled == null) {
+            try {
+                dump(fd, pw, args);
+            } catch (SecurityException e) {
+                pw.println("Security exception: " + e.getMessage());
+                throw e;
+            } catch (Throwable e) {
+                // Unlike usual calls, in this case if an exception gets thrown
+                // back to us we want to print it back in to the dump data, since
+                // that is where the caller expects all interesting information to
+                // go.
+                pw.println();
+                pw.println("Exception occurred while dumping:");
+                e.printStackTrace(pw);
+            }
+        } else {
+            pw.println(sDumpDisabled);
+        }
+    }
+
+    /**
+     * Like {@link #dump(FileDescriptor, String[])}, but ensures the target
+     * executes asynchronously.
+     */
+    public void dumpAsync(@NonNull final FileDescriptor fd, @Nullable final String[] args) {
+        final FileOutputStream fout = new FileOutputStream(fd);
+        final PrintWriter pw = new FastPrintWriter(fout);
+        Thread thr = new Thread("Binder.dumpAsync") {
+            public void run() {
+                try {
+                    dump(fd, pw, args);
+                } finally {
+                    pw.flush();
+                }
+            }
+        };
+        thr.start();
+    }
+
+    /**
+     * Print the object's state into the given stream.
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param fout The file to which you should dump your state. This will be
+     * closed for you after you return.
+     * @param args additional arguments to the dump request.
+     */
+    protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
+            @Nullable String[] args) {
+    }
+
+    /**
+     * @param in The raw file descriptor that an input data stream can be read from.
+     * @param out The raw file descriptor that normal command messages should be written to.
+     * @param err The raw file descriptor that command error messages should be written to.
+     * @param args Command-line arguments.
+     * @param callback Callback through which to interact with the invoking shell.
+     * @param resultReceiver Called when the command has finished executing, with the result code.
+     * @throws RemoteException
+     *
+     * @hide
+     */
+    public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err,
+            @NonNull String[] args, @Nullable ShellCallback callback,
+            @NonNull ResultReceiver resultReceiver) throws RemoteException {
+        onShellCommand(in, out, err, args, callback, resultReceiver);
+    }
+
+    /**
+     * Handle a call to {@link #shellCommand}.
+     *
+     * <p>The default implementation performs a caller check to make sure the caller UID is of
+     * SHELL or ROOT, and then call {@link #handleShellCommand}.
+     *
+     * <p class="caution">Note: no permission checking is done before calling this method; you must
+     * apply any security checks as appropriate for the command being executed.
+     * Consider using {@link ShellCommand} to help in the implementation.
+     *
+     * @hide
+     */
+    public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err,
+            @NonNull String[] args, @Nullable ShellCallback callback,
+            @NonNull ResultReceiver resultReceiver) throws RemoteException {
+
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
+            resultReceiver.send(-1, null);
+            throw new SecurityException("Shell commands are only callable by ADB");
+        }
+
+        // First, convert in, out and err to @NonNull, by redirecting any that's null to /dev/null.
+        try {
+            if (in == null) {
+                in = new FileInputStream("/dev/null").getFD();
+            }
+            if (out == null) {
+                out = new FileOutputStream("/dev/null").getFD();
+            }
+            if (err == null) {
+                err = out;
+            }
+        } catch (IOException e) {
+            PrintWriter pw = new FastPrintWriter(new FileOutputStream(err != null ? err : out));
+            pw.println("Failed to open /dev/null: " + e.getMessage());
+            pw.flush();
+            resultReceiver.send(-1, null);
+            return;
+        }
+        // Also make args @NonNull.
+        if (args == null) {
+            args = new String[0];
+        }
+
+        int result = -1;
+        try (ParcelFileDescriptor inPfd = ParcelFileDescriptor.dup(in);
+                ParcelFileDescriptor outPfd = ParcelFileDescriptor.dup(out);
+                ParcelFileDescriptor errPfd = ParcelFileDescriptor.dup(err)) {
+            result = handleShellCommand(inPfd, outPfd, errPfd, args);
+        } catch (IOException e) {
+            PrintWriter pw = new FastPrintWriter(new FileOutputStream(err));
+            pw.println("dup() failed: " + e.getMessage());
+            pw.flush();
+        } finally {
+            resultReceiver.send(result, null);
+        }
+    }
+
+    /**
+     * System services can implement this method to implement ADB shell commands.
+     *
+     * <p>A system binder service can implement it to handle shell commands on ADB. For example,
+     * the Job Scheduler service implements it to handle {@code adb shell cmd jobscheduler}.
+     *
+     * <p>Commands are only executable by ADB shell; i.e. only {@link Process#SHELL_UID} and
+     * {@link Process#ROOT_UID} can call them.
+     *
+     * @param in standard input
+     * @param out standard output
+     * @param err standard error
+     * @param args arguments passed to the command. Can be empty. The first argument is typically
+     * a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}.
+     * @return the status code returned from the {@code cmd} command.
+     *
+     * @hide
+     */
+    @SystemApi
+    public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+            @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+            @NonNull String[] args) {
+        FileOutputStream ferr = new FileOutputStream(err.getFileDescriptor());
+        PrintWriter pw = new FastPrintWriter(ferr);
+        pw.println("No shell command implementation.");
+        pw.flush();
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public final native @Nullable IBinder getExtension();
+
+    /**
+     * Set the binder extension.
+     * This should be called immediately when the object is created.
+     *
+     * @hide
+     */
+    public final native void setExtension(@Nullable IBinder extension);
+
+    /**
+     * Default implementation rewinds the parcels and calls onTransact. On
+     * the remote side, transact calls into the binder to do the IPC.
+     */
+    public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
+            int flags) throws RemoteException {
+        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
+
+        if (data != null) {
+            data.setDataPosition(0);
+        }
+        boolean r = onTransact(code, data, reply, flags);
+        if (reply != null) {
+            reply.setDataPosition(0);
+        }
+        return r;
+    }
+
+    /**
+     * Local implementation is a no-op.
+     */
+    public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+    }
+
+    /**
+     * Local implementation is a no-op.
+     */
+    public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
+        return true;
+    }
+
+    static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
+        if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
+            // Trying to send > 800k, this is way too much.
+            StringBuilder sb = new StringBuilder();
+            sb.append(msg);
+            sb.append(": on ");
+            sb.append(obj);
+            sb.append(" calling ");
+            sb.append(code);
+            sb.append(" size ");
+            sb.append(parcel.dataSize());
+            sb.append(" (data: ");
+            parcel.setDataPosition(0);
+            sb.append(parcel.readInt());
+            sb.append(", ");
+            sb.append(parcel.readInt());
+            sb.append(", ");
+            sb.append(parcel.readInt());
+            sb.append(")");
+            Slog.wtfStack(TAG, sb.toString());
+        }
+    }
+
+    private static native long getNativeBBinderHolder();
+
+    /**
+     * By default, we use the calling UID since we can always trust it.
+     */
+    private static volatile BinderInternal.WorkSourceProvider sWorkSourceProvider =
+            (x) -> Binder.getCallingUid();
+
+    /**
+     * Sets the work source provider.
+     *
+     * <li>The callback is global. Only fast operations should be done to avoid thread
+     * contentions.
+     * <li>The callback implementation needs to handle synchronization if needed. The methods on the
+     * callback can be called concurrently.
+     * <li>The callback is called on the critical path of the binder transaction so be careful about
+     * performance.
+     * <li>Never execute another binder transaction inside the callback.
+     *
+     * @hide
+     */
+    public static void setWorkSourceProvider(BinderInternal.WorkSourceProvider workSourceProvider) {
+        if (workSourceProvider == null) {
+            throw new IllegalArgumentException("workSourceProvider cannot be null");
+        }
+        sWorkSourceProvider = workSourceProvider;
+    }
+
+    // Entry point from android_util_Binder.cpp's onTransact.
+    @UnsupportedAppUsage
+    private boolean execTransact(int code, long dataObj, long replyObj,
+            int flags) {
+
+        Parcel data = Parcel.obtain(dataObj);
+        Parcel reply = Parcel.obtain(replyObj);
+
+        // At that point, the parcel request headers haven't been parsed so we do not know what
+        // {@link WorkSource} the caller has set. Use calling UID as the default.
+        //
+        // TODO: this is wrong - we should attribute along the entire call route
+        // also this attribution logic should move to native code - it only works
+        // for Java now
+        //
+        // This attribution support is not generic and therefore not support in RPC mode
+        final int callingUid = data.isForRpc() ? -1 : Binder.getCallingUid();
+        final long origWorkSource = callingUid == -1
+                ? -1 : ThreadLocalWorkSource.setUid(callingUid);
+
+        try {
+            return execTransactInternal(code, data, reply, flags, callingUid);
+        } finally {
+            reply.recycle();
+            data.recycle();
+
+            if (callingUid != -1) {
+                ThreadLocalWorkSource.restore(origWorkSource);
+            }
+        }
+    }
+
+    private boolean execTransactInternal(int code, Parcel data, Parcel reply, int flags,
+            int callingUid) {
+        // Make sure the observer won't change while processing a transaction.
+        final BinderInternal.Observer observer = sObserver;
+        final CallSession callSession =
+                observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
+        // Theoretically, we should call transact, which will call onTransact,
+        // but all that does is rewind it, and we just got these from an IPC,
+        // so we'll just call it directly.
+        boolean res;
+        // Log any exceptions as warnings, don't silently suppress them.
+        // If the call was {@link IBinder#FLAG_ONEWAY} then these exceptions
+        // disappear into the ether.
+        final boolean tagEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_AIDL);
+        final boolean hasFullyQualifiedName = getMaxTransactionId() > 0;
+        final String transactionTraceName;
+
+        if (tagEnabled && hasFullyQualifiedName) {
+            // If tracing enabled and we have a fully qualified name, fetch the name
+            transactionTraceName = getTransactionTraceName(code);
+        } else if (tagEnabled && isStackTrackingEnabled()) {
+            // If tracing is enabled and we *don't* have a fully qualified name, fetch the
+            // 'best effort' name only for stack tracking. This works around noticeable perf impact
+            // on low latency binder calls (<100us). The tracing call itself is between (1-10us) and
+            // the perf impact can be quite noticeable while benchmarking such binder calls.
+            // The primary culprits are ContentProviders and Cursors which convenienty don't
+            // autogenerate their AIDL and hence will not have a fully qualified name.
+            //
+            // TODO(b/253426478): Relax this constraint after a more robust fix
+            transactionTraceName = getTransactionTraceName(code);
+        } else {
+            transactionTraceName = null;
+        }
+
+        final boolean tracingEnabled = tagEnabled && transactionTraceName != null;
+        try {
+            // TODO - this logic should not be in Java - it should be in native
+            // code in libbinder so that it works for all binder users.
+            final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
+            if (heavyHitterWatcher != null && callingUid != -1) {
+                // Notify the heavy hitter watcher, if it's enabled.
+                heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
+            }
+            if (tracingEnabled) {
+                Trace.traceBegin(Trace.TRACE_TAG_AIDL, transactionTraceName);
+            }
+
+            // TODO - this logic should not be in Java - it should be in native
+            // code in libbinder so that it works for all binder users. Further,
+            // this should not re-use flags.
+            if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0 && callingUid != -1) {
+                AppOpsManager.startNotedAppOpsCollection(callingUid);
+                try {
+                    res = onTransact(code, data, reply, flags);
+                } finally {
+                    AppOpsManager.finishNotedAppOpsCollection();
+                }
+            } else {
+                res = onTransact(code, data, reply, flags);
+            }
+        } catch (RemoteException|RuntimeException e) {
+            if (observer != null) {
+                observer.callThrewException(callSession, e);
+            }
+            if (LOG_RUNTIME_EXCEPTION) {
+                Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
+            }
+            if ((flags & FLAG_ONEWAY) != 0) {
+                if (e instanceof RemoteException) {
+                    Log.w(TAG, "Binder call failed.", e);
+                } else {
+                    Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
+                }
+            } else {
+                // Clear the parcel before writing the exception.
+                reply.setDataSize(0);
+                reply.setDataPosition(0);
+                reply.writeException(e);
+            }
+            res = true;
+        } finally {
+            if (tracingEnabled) {
+                Trace.traceEnd(Trace.TRACE_TAG_AIDL);
+            }
+            if (observer != null) {
+                // The parcel RPC headers have been called during onTransact so we can now access
+                // the worksource UID from the parcel.
+                final int workSourceUid = sWorkSourceProvider.resolveWorkSourceUid(
+                        data.readCallingWorkSourceUid());
+                observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
+            }
+
+            checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
+        }
+
+        // Just in case -- we are done with the IPC, so there should be no more strict
+        // mode violations that have gathered for this thread. Either they have been
+        // parceled and are now in transport off to the caller, or we are returning back
+        // to the main transaction loop to wait for another incoming transaction. Either
+        // way, strict mode begone!
+        StrictMode.clearGatheredViolations();
+        return res;
+    }
+
+    /**
+     * Set the configuration for the heavy hitter watcher.
+     *
+     * @hide
+     */
+    public static synchronized void setHeavyHitterWatcherConfig(final boolean enabled,
+            final int batchSize, final float threshold,
+            @Nullable final BinderCallHeavyHitterListener listener) {
+        Slog.i(TAG, "Setting heavy hitter watcher config: "
+                + enabled + ", " + batchSize + ", " + threshold);
+        BinderCallHeavyHitterWatcher watcher = sHeavyHitterWatcher;
+        if (enabled) {
+            if (listener == null) {
+                throw new IllegalArgumentException();
+            }
+            boolean newWatcher = false;
+            if (watcher == null) {
+                watcher = BinderCallHeavyHitterWatcher.getInstance();
+                newWatcher = true;
+            }
+            watcher.setConfig(true, batchSize, threshold, listener);
+            if (newWatcher) {
+                sHeavyHitterWatcher = watcher;
+            }
+        } else if (watcher != null) {
+            watcher.setConfig(false, 0, 0.0f, null);
+        }
+    }
+}
diff --git a/android-34/android/os/BinderCallsStatsPerfTest.java b/android-34/android/os/BinderCallsStatsPerfTest.java
new file mode 100644
index 0000000..12e49e3
--- /dev/null
+++ b/android-34/android/os/BinderCallsStatsPerfTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal.CallSession;
+import com.android.internal.os.CachedDeviceState;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Performance tests for {@link BinderCallsStats}
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BinderCallsStatsPerfTest {
+    private static final int DEFAULT_BUCKET_SIZE = 1000;
+    private static final int WORKSOURCE_UID = 1;
+    static class FakeCpuTimeBinderCallsStats extends BinderCallsStats {
+        private int mTimeMs;
+
+        FakeCpuTimeBinderCallsStats() {
+            super(new BinderCallsStats.Injector());
+            setDeviceState(new CachedDeviceState(false, false).getReadonlyClient());
+        }
+
+        protected long getThreadTimeMicro() {
+            return mTimeMs++;
+        }
+
+        protected long getElapsedRealtimeMicro() {
+            return mTimeMs++;
+        }
+    }
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    private BinderCallsStats mBinderCallsStats;
+
+    @Before
+    public void setUp() {
+        mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
+        CachedDeviceState deviceState = new CachedDeviceState(false, false);
+        mBinderCallsStats.setDeviceState(deviceState.getReadonlyClient());
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void timeCallSession() {
+        mBinderCallsStats.setDetailedTracking(true);
+        runScenario(DEFAULT_BUCKET_SIZE);
+    }
+
+    @Test
+    public void timeCallSessionOnePercentSampling() {
+        mBinderCallsStats.setDetailedTracking(false);
+        mBinderCallsStats.setSamplingInterval(100);
+        runScenario(DEFAULT_BUCKET_SIZE);
+    }
+
+    @Test
+    public void timeCallSessionTrackingDisabled() {
+        mBinderCallsStats.setDetailedTracking(false);
+        runScenario(DEFAULT_BUCKET_SIZE);
+    }
+
+    @Test
+    public void timeCallSession_1000_buckets_cpuNotRecorded() {
+        mBinderCallsStats = new FakeCpuTimeBinderCallsStats();
+        mBinderCallsStats.setSamplingInterval(1);
+        runScenario(/* max bucket size */ 1000);
+    }
+
+    @Test
+    public void timeCallSession_500_buckets_cpuNotRecorded() {
+        mBinderCallsStats = new FakeCpuTimeBinderCallsStats();
+        mBinderCallsStats.setSamplingInterval(1);
+        runScenario(/* max bucket size */ 500);
+    }
+
+    @Test
+    public void timeCallSession_100_buckets_cpuNotRecorded() {
+        mBinderCallsStats = new FakeCpuTimeBinderCallsStats();
+        mBinderCallsStats.setSamplingInterval(1);
+        runScenario(/* max bucket size */ 100);
+    }
+
+    // There will be a warmup time of maxBucketSize to initialize the map of CallStat.
+    private void runScenario(int maxBucketSize) {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Binder b = new Binder();
+        while (state.keepRunning()) {
+            for (int i = 0; i < 10000; i++) {
+                CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize, WORKSOURCE_UID);
+                mBinderCallsStats.callEnded(s, 0, 0, WORKSOURCE_UID);
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/BinderProxy.java b/android-34/android/os/BinderProxy.java
new file mode 100644
index 0000000..1929a4d
--- /dev/null
+++ b/android-34/android/os/BinderProxy.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderInternal;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Java proxy for a native IBinder object.
+ * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
+ * directly from Java code.
+ *
+ * @hide
+ */
+public final class BinderProxy implements IBinder {
+    // See android_util_Binder.cpp for the native half of this.
+
+    // Assume the process-wide default value when created
+    volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
+
+    private static volatile Binder.ProxyTransactListener sTransactListener = null;
+
+    private static class BinderProxyMapSizeException extends AssertionError {
+        BinderProxyMapSizeException(String s) {
+            super(s);
+        }
+    };
+
+    /**
+     * @see {@link Binder#setProxyTransactListener(listener)}.
+     */
+    public static void setTransactListener(@Nullable Binder.ProxyTransactListener listener) {
+        sTransactListener = listener;
+    }
+
+    /*
+     * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
+     * We roll our own only because we need to lazily remove WeakReferences during accesses
+     * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
+     * because we want weak values, not keys.
+     * Our hash table is never resized, but the number of entries is unlimited;
+     * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
+     * Not thread-safe. Client ensures there's a single access at a time.
+     */
+    private static final class ProxyMap {
+        private static final int LOG_MAIN_INDEX_SIZE = 8;
+        private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
+        private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
+        /**
+         * Debuggable builds will throw an BinderProxyMapSizeException if the number of
+         * map entries exceeds:
+         */
+        private static final int CRASH_AT_SIZE = 25_000;
+
+        /**
+         * We next warn when we exceed this bucket size.
+         */
+        private int mWarnBucketSize = 20;
+
+        /**
+         * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
+         */
+        private static final int WARN_INCREMENT = 10;
+
+        /**
+         * Hash function tailored to native pointers.
+         * Returns a value < MAIN_INDEX_SIZE.
+         */
+        private static int hash(long arg) {
+            return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
+        }
+
+        /**
+         * Return the total number of pairs in the map.
+         */
+        private int size() {
+            int size = 0;
+            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                if (a != null) {
+                    size += a.size();
+                }
+            }
+            return size;
+        }
+
+        /**
+         * Return the total number of pairs in the map containing values that have
+         * not been cleared. More expensive than the above size function.
+         */
+        private int unclearedSize() {
+            int size = 0;
+            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                if (a != null) {
+                    for (WeakReference<BinderProxy> ref : a) {
+                        if (!ref.refersTo(null)) {
+                            ++size;
+                        }
+                    }
+                }
+            }
+            return size;
+        }
+
+        /**
+         * Remove ith entry from the hash bucket indicated by hash.
+         */
+        private void remove(int hash, int index) {
+            Long[] keyArray = mMainIndexKeys[hash];
+            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
+            int size = valueArray.size();  // KeyArray may have extra elements.
+            // Move last entry into empty slot, and truncate at end.
+            if (index != size - 1) {
+                keyArray[index] = keyArray[size - 1];
+                valueArray.set(index, valueArray.get(size - 1));
+            }
+            valueArray.remove(size - 1);
+            // Just leave key array entry; it's unused. We only trust the valueArray size.
+        }
+
+        /**
+         * Look up the supplied key. If we have a non-cleared entry for it, return it.
+         */
+        BinderProxy get(long key) {
+            int myHash = hash(key);
+            Long[] keyArray = mMainIndexKeys[myHash];
+            if (keyArray == null) {
+                return null;
+            }
+            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+            int bucketSize = valueArray.size();
+            for (int i = 0; i < bucketSize; ++i) {
+                long foundKey = keyArray[i];
+                if (key == foundKey) {
+                    WeakReference<BinderProxy> wr = valueArray.get(i);
+                    BinderProxy bp = wr.get();
+                    if (bp != null) {
+                        return bp;
+                    } else {
+                        remove(myHash, i);
+                        return null;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private int mRandom;  // A counter used to generate a "random" index. World's 2nd worst RNG.
+
+        /**
+         * Add the key-value pair to the map.
+         * Requires that the indicated key is not already in the map.
+         */
+        void set(long key, @NonNull BinderProxy value) {
+            int myHash = hash(key);
+            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+            if (valueArray == null) {
+                valueArray = mMainIndexValues[myHash] = new ArrayList<>();
+                mMainIndexKeys[myHash] = new Long[1];
+            }
+            int size = valueArray.size();
+            WeakReference<BinderProxy> newWr = new WeakReference<>(value);
+            // First look for a cleared reference.
+            // This ensures that ArrayList size is bounded by the maximum occupancy of
+            // that bucket.
+            for (int i = 0; i < size; ++i) {
+                if (valueArray.get(i).refersTo(null)) {
+                    valueArray.set(i, newWr);
+                    Long[] keyArray = mMainIndexKeys[myHash];
+                    keyArray[i] = key;
+                    if (i < size - 1) {
+                        // "Randomly" check one of the remaining entries in [i+1, size), so that
+                        // needlessly long buckets are eventually pruned.
+                        int rnd = Math.floorMod(++mRandom, size - (i + 1));
+                        if (valueArray.get(i + 1 + rnd).refersTo(null)) {
+                            remove(myHash, i + 1 + rnd);
+                        }
+                    }
+                    return;
+                }
+            }
+            valueArray.add(size, newWr);
+            Long[] keyArray = mMainIndexKeys[myHash];
+            if (keyArray.length == size) {
+                // size >= 1, since we initially allocated one element
+                Long[] newArray = new Long[size + size / 2 + 2];
+                System.arraycopy(keyArray, 0, newArray, 0, size);
+                newArray[size] = key;
+                mMainIndexKeys[myHash] = newArray;
+            } else {
+                keyArray[size] = key;
+            }
+            if (size >= mWarnBucketSize) {
+                final int totalSize = size();
+                Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
+                        + " total = " + totalSize);
+                mWarnBucketSize += WARN_INCREMENT;
+                if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+                    // Use the number of uncleared entries to determine whether we should
+                    // really report a histogram and crash. We don't want to fundamentally
+                    // change behavior for a debuggable process, so we GC only if we are
+                    // about to crash.
+                    final int totalUnclearedSize = unclearedSize();
+                    if (totalUnclearedSize >= CRASH_AT_SIZE) {
+                        dumpProxyInterfaceCounts();
+                        dumpPerUidProxyCounts();
+                        Runtime.getRuntime().gc();
+                        throw new BinderProxyMapSizeException(
+                                "Binder ProxyMap has too many entries: "
+                                + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+                                + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
+                    } else if (totalSize > 3 * totalUnclearedSize / 2) {
+                        Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
+                                + (totalSize - totalUnclearedSize) + " of " + totalSize
+                                + " are cleared");
+                    }
+                }
+            }
+        }
+
+        private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) {
+            if (maxToReturn < 0) {
+                throw new IllegalArgumentException("negative interface count");
+            }
+
+            Map<String, Integer> counts = new HashMap<>();
+            final ArrayList<WeakReference<BinderProxy>> proxiesToQuery =
+                    new ArrayList<WeakReference<BinderProxy>>();
+            synchronized (sProxyMap) {
+                for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                    if (a != null) {
+                        proxiesToQuery.addAll(a);
+                    }
+                }
+            }
+            // For gathering this debug output, we're making synchronous binder calls
+            // out of system_server to all processes hosting binder objects it holds a reference to;
+            // since some of those processes might be frozen, we don't want to block here
+            // forever. Disable the freezer.
+            try {
+                ActivityManager.getService().enableAppFreezer(false);
+            } catch (RemoteException e) {
+                Log.e(Binder.TAG, "RemoteException while disabling app freezer");
+            }
+
+            // We run the dump on a separate thread, because there are known cases where
+            // a process overrides getInterfaceDescriptor() and somehow blocks on it, causing
+            // the calling thread (usually AMS) to hit the watchdog.
+            // Do the dumping on a separate thread instead, and give up after a while.
+            ExecutorService executorService = Executors.newSingleThreadExecutor();
+            executorService.submit(() -> {
+                for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
+                    BinderProxy bp = weakRef.get();
+                    String key;
+                    if (bp == null) {
+                        key = "<cleared weak-ref>";
+                    } else {
+                        try {
+                            key = bp.getInterfaceDescriptor();
+                            if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
+                                key = "<proxy to dead node>";
+                            }
+                        } catch (Throwable t) {
+                            key = "<exception during getDescriptor>";
+                        }
+                    }
+                    Integer i = counts.get(key);
+                    if (i == null) {
+                        counts.put(key, 1);
+                    } else {
+                        counts.put(key, i + 1);
+                    }
+                }
+            });
+
+            try {
+                executorService.shutdown();
+                boolean dumpDone = executorService.awaitTermination(20, TimeUnit.SECONDS);
+                if (!dumpDone) {
+                    Log.e(Binder.TAG, "Failed to complete binder proxy dump,"
+                            + " dumping what we have so far.");
+                }
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+            try {
+                ActivityManager.getService().enableAppFreezer(true);
+            } catch (RemoteException e) {
+                Log.e(Binder.TAG, "RemoteException while re-enabling app freezer");
+            }
+            Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
+                    new Map.Entry[counts.size()]);
+
+            Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
+                    -> b.getValue().compareTo(a.getValue()));
+
+            int returnCount = Math.min(maxToReturn, sorted.length);
+            InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount];
+            for (int i = 0; i < returnCount; i++) {
+                ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue());
+            }
+            return ifaceCounts;
+        }
+
+        static final int MAX_NUM_INTERFACES_TO_DUMP = 10;
+
+        /**
+         * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
+         */
+        private void dumpProxyInterfaceCounts() {
+            final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP);
+
+            Log.v(Binder.TAG, "BinderProxy descriptor histogram "
+                    + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):");
+            for (int i = 0; i < sorted.length; i++) {
+                Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]);
+            }
+        }
+
+        /**
+         * Dump per uid binder proxy counts to the logcat.
+         */
+        private void dumpPerUidProxyCounts() {
+            SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+            if (counts.size() == 0) return;
+            Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
+            for (int i = 0; i < counts.size(); i++) {
+                final int uid = counts.keyAt(i);
+                final int binderCount = counts.valueAt(i);
+                Log.d(Binder.TAG, "UID : " + uid + "  count = " + binderCount);
+            }
+        }
+
+        // Corresponding ArrayLists in the following two arrays always have the same size.
+        // They contain no empty entries. However WeakReferences in the values ArrayLists
+        // may have been cleared.
+
+        // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
+        // The values ArrayList has the proper size(), the corresponding keys array
+        // is always at least the same size, but may be larger.
+        // If either a particular keys array, or the corresponding values ArrayList
+        // are null, then they both are.
+        private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
+        private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
+                new ArrayList[MAIN_INDEX_SIZE];
+    }
+
+    @GuardedBy("sProxyMap")
+    private static final ProxyMap sProxyMap = new ProxyMap();
+
+    /**
+     * Simple pair-value class to store number of binder proxy interfaces live in this process.
+     */
+    public static final class InterfaceCount {
+        private final String mInterfaceName;
+        private final int mCount;
+
+        InterfaceCount(String interfaceName, int count) {
+            mInterfaceName = interfaceName;
+            mCount = count;
+        }
+
+        @Override
+        public String toString() {
+            return mInterfaceName + " x" + Integer.toString(mCount);
+        }
+    }
+
+    /**
+     * Get a sorted array with entries mapping proxy interface names to the number
+     * of live proxies with those names.
+     *
+     * @param num maximum number of proxy interface counts to return. Use
+     *            Integer.MAX_VALUE to retrieve all
+     * @hide
+     */
+    public static InterfaceCount[] getSortedInterfaceCounts(int num) {
+        return sProxyMap.getSortedInterfaceCounts(num);
+    }
+
+    /**
+     * Returns the number of binder proxies held in this process.
+     * @return number of binder proxies in this process
+     */
+    public static int getProxyCount() {
+        synchronized (sProxyMap) {
+            return sProxyMap.size();
+        }
+    }
+
+    /**
+     * Dump proxy debug information.
+     *
+     * @hide
+     */
+    public static void dumpProxyDebugInfo() {
+        if (Build.IS_DEBUGGABLE) {
+            sProxyMap.dumpProxyInterfaceCounts();
+            sProxyMap.dumpPerUidProxyCounts();
+        }
+    }
+
+    /**
+     * Return a BinderProxy for IBinder.
+     * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
+     * in use, then we return the same bp.
+     *
+     * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
+     * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
+     * we exit via an exception.  If neither applies, it's the callers responsibility to
+     * recycle nativeData.
+     * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
+     */
+    private static BinderProxy getInstance(long nativeData, long iBinder) {
+        BinderProxy result;
+        synchronized (sProxyMap) {
+            try {
+                result = sProxyMap.get(iBinder);
+                if (result != null) {
+                    return result;
+                }
+                result = new BinderProxy(nativeData);
+            } catch (Throwable e) {
+                // We're throwing an exception (probably OOME); don't drop nativeData.
+                NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
+                        nativeData);
+                throw e;
+            }
+            NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
+            // The registry now owns nativeData, even if registration threw an exception.
+            sProxyMap.set(iBinder, result);
+        }
+        return result;
+    }
+
+    private BinderProxy(long nativeData) {
+        mNativeData = nativeData;
+    }
+
+    /**
+     * Guestimate of native memory associated with a BinderProxy.
+     * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
+     * that points back to us. We guess high since it includes a GlobalRef, which
+     * may be in short supply.
+     */
+    private static final int NATIVE_ALLOCATION_SIZE = 1000;
+
+    // Use a Holder to allow static initialization of BinderProxy in the boot image, and
+    // to avoid some initialization ordering issues.
+    private static class NoImagePreloadHolder {
+        public static final long sNativeFinalizer = getNativeFinalizer();
+        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
+    }
+
+    /**
+     * @return false if the hosting process is gone, otherwise whatever the remote returns
+     */
+    public native boolean pingBinder();
+
+    /**
+     * @return false if the hosting process is gone
+     */
+    public native boolean isBinderAlive();
+
+    /**
+     * Retrieve a local interface - always null in case of a proxy
+     */
+    public IInterface queryLocalInterface(String descriptor) {
+        return null;
+    }
+
+    /** @hide */
+    @Override
+    public native @Nullable IBinder getExtension() throws RemoteException;
+
+    /**
+     * Perform a binder transaction on a proxy.
+     *
+     * @param code The action to perform.  This should
+     * be a number between {@link #FIRST_CALL_TRANSACTION} and
+     * {@link #LAST_CALL_TRANSACTION}.
+     * @param data Marshalled data to send to the target.  Must not be null.
+     * If you are not sending any data, you must create an empty Parcel
+     * that is given here.
+     * @param reply Marshalled data to be received from the target.  May be
+     * null if you are not interested in the return value.
+     * @param flags Additional operation flags.  Either 0 for a normal
+     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+     *
+     * @return
+     * @throws RemoteException
+     */
+    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
+
+        boolean warnOnBlocking = mWarnOnBlocking; // Cache it to reduce volatile access.
+
+        if (warnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
+                && Binder.sWarnOnBlockingOnCurrentThread.get()) {
+
+            // For now, avoid spamming the log by disabling after we've logged
+            // about this interface at least once
+            mWarnOnBlocking = false;
+            warnOnBlocking = false;
+
+            if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                // Log this as a WTF on userdebug and eng builds.
+                Log.wtf(Binder.TAG,
+                        "Outgoing transactions from this process must be FLAG_ONEWAY",
+                        new Throwable());
+            } else {
+                Log.w(Binder.TAG,
+                        "Outgoing transactions from this process must be FLAG_ONEWAY",
+                        new Throwable());
+            }
+        }
+
+        final boolean tracingEnabled = Binder.isStackTrackingEnabled();
+        if (tracingEnabled) {
+            final Throwable tr = new Throwable();
+            Binder.getTransactionTracker().addTrace(tr);
+            StackTraceElement stackTraceElement = tr.getStackTrace()[1];
+            Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
+                    stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
+        }
+
+        // Make sure the listener won't change while processing a transaction.
+        final Binder.ProxyTransactListener transactListener = sTransactListener;
+        Object session = null;
+
+        if (transactListener != null) {
+            final int origWorkSourceUid = Binder.getCallingWorkSourceUid();
+            session = transactListener.onTransactStarted(this, code, flags);
+
+            // Allow the listener to update the work source uid. We need to update the request
+            // header if the uid is updated.
+            final int updatedWorkSourceUid = Binder.getCallingWorkSourceUid();
+            if (origWorkSourceUid != updatedWorkSourceUid) {
+                data.replaceCallingWorkSourceUid(updatedWorkSourceUid);
+            }
+        }
+
+        final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
+                AppOpsManager.pauseNotedAppOpsCollection();
+
+        if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
+            flags |= FLAG_COLLECT_NOTED_APP_OPS;
+        }
+
+        try {
+            final boolean result = transactNative(code, data, reply, flags);
+
+            if (reply != null && !warnOnBlocking) {
+                reply.addFlags(Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);
+            }
+
+            return result;
+        } finally {
+            AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
+
+            if (transactListener != null) {
+                transactListener.onTransactEnded(session);
+            }
+
+            if (tracingEnabled) {
+                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+            }
+        }
+    }
+
+    /* Returns the native free function */
+    private static native long getNativeFinalizer();
+    /**
+     *  See {@link IBinder#getInterfaceDescriptor()}
+     */
+    public native String getInterfaceDescriptor() throws RemoteException;
+
+    /**
+     * Native implementation of transact() for proxies
+     */
+    public native boolean transactNative(int code, Parcel data, Parcel reply,
+            int flags) throws RemoteException;
+    /**
+     * See {@link IBinder#linkToDeath(DeathRecipient, int)}
+     */
+    public native void linkToDeath(DeathRecipient recipient, int flags)
+            throws RemoteException;
+    /**
+     * See {@link IBinder#unlinkToDeath}
+     */
+    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+
+    /**
+     * Perform a dump on the remote object
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param args additional arguments to the dump request.
+     * @throws RemoteException
+     */
+    public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeFileDescriptor(fd);
+        data.writeStringArray(args);
+        try {
+            transact(DUMP_TRANSACTION, data, reply, 0);
+            reply.readException();
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    /**
+     * Perform an asynchronous dump on the remote object
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param args additional arguments to the dump request.
+     * @throws RemoteException
+     */
+    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeFileDescriptor(fd);
+        data.writeStringArray(args);
+        try {
+            transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    /**
+     * See {@link IBinder#shellCommand(FileDescriptor, FileDescriptor, FileDescriptor,
+     * String[], ShellCallback, ResultReceiver)}
+     *
+     * @param in The raw file descriptor that an input data stream can be read from.
+     * @param out The raw file descriptor that normal command messages should be written to.
+     * @param err The raw file descriptor that command error messages should be written to.
+     * @param args Command-line arguments.
+     * @param callback Optional callback to the caller's shell to perform operations in it.
+     * @param resultReceiver Called when the command has finished executing, with the result code.
+     * @throws RemoteException
+     */
+    public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeFileDescriptor(in);
+        data.writeFileDescriptor(out);
+        data.writeFileDescriptor(err);
+        data.writeStringArray(args);
+        ShellCallback.writeToParcel(callback, data);
+        resultReceiver.writeToParcel(data, 0);
+        try {
+            transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
+            reply.readException();
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    private static void sendDeathNotice(DeathRecipient recipient, IBinder binderProxy) {
+        if (false) {
+            Log.v("JavaBinder", "sendDeathNotice to " + recipient + " for " + binderProxy);
+        }
+        try {
+            recipient.binderDied(binderProxy);
+        } catch (RuntimeException exc) {
+            Log.w("BinderNative", "Uncaught exception from death notification",
+                    exc);
+        }
+    }
+
+    /**
+     * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
+     * native IBinder object, and a DeathRecipientList.
+     */
+    private final long mNativeData;
+}
diff --git a/android-34/android/os/BluetoothBatteryStats.java b/android-34/android/os/BluetoothBatteryStats.java
new file mode 100644
index 0000000..3d99a08
--- /dev/null
+++ b/android-34/android/os/BluetoothBatteryStats.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of Bluetooth battery stats.
+ *
+ * @hide
+ */
+public class BluetoothBatteryStats implements Parcelable {
+
+    /** @hide */
+    public static class UidStats {
+        public final int uid;
+        public final long scanTimeMs;
+        public final long unoptimizedScanTimeMs;
+        public final int scanResultCount;
+        public final long rxTimeMs;
+        public final long txTimeMs;
+
+        public UidStats(int uid, long scanTimeMs, long unoptimizedScanTimeMs, int scanResultCount,
+                long rxTimeMs, long txTimeMs) {
+            this.uid = uid;
+            this.scanTimeMs = scanTimeMs;
+            this.unoptimizedScanTimeMs = unoptimizedScanTimeMs;
+            this.scanResultCount = scanResultCount;
+            this.rxTimeMs = rxTimeMs;
+            this.txTimeMs = txTimeMs;
+        }
+
+        private UidStats(Parcel in) {
+            uid = in.readInt();
+            scanTimeMs = in.readLong();
+            unoptimizedScanTimeMs = in.readLong();
+            scanResultCount = in.readInt();
+            rxTimeMs = in.readLong();
+            txTimeMs = in.readLong();
+        }
+
+        private void writeToParcel(Parcel out) {
+            out.writeInt(uid);
+            out.writeLong(scanTimeMs);
+            out.writeLong(unoptimizedScanTimeMs);
+            out.writeInt(scanResultCount);
+            out.writeLong(rxTimeMs);
+            out.writeLong(txTimeMs);
+        }
+
+        @Override
+        public String toString() {
+            return "UidStats{"
+                    + "uid=" + uid
+                    + ", scanTimeMs=" + scanTimeMs
+                    + ", unoptimizedScanTimeMs=" + unoptimizedScanTimeMs
+                    + ", scanResultCount=" + scanResultCount
+                    + ", rxTimeMs=" + rxTimeMs
+                    + ", txTimeMs=" + txTimeMs
+                    + '}';
+        }
+    }
+
+    private final List<UidStats> mUidStats;
+
+    public BluetoothBatteryStats(@NonNull List<UidStats> uidStats) {
+        mUidStats = uidStats;
+    }
+
+    @NonNull
+    public List<UidStats> getUidStats() {
+        return mUidStats;
+    }
+
+    protected BluetoothBatteryStats(Parcel in) {
+        final int size = in.readInt();
+        mUidStats = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            mUidStats.add(new UidStats(in));
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        final int size = mUidStats.size();
+        out.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            UidStats stats = mUidStats.get(i);
+            stats.writeToParcel(out);
+        }
+    }
+
+    public static final Creator<BluetoothBatteryStats> CREATOR =
+            new Creator<BluetoothBatteryStats>() {
+                @Override
+                public BluetoothBatteryStats createFromParcel(Parcel in) {
+                    return new BluetoothBatteryStats(in);
+                }
+
+                @Override
+                public BluetoothBatteryStats[] newArray(int size) {
+                    return new BluetoothBatteryStats[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-34/android/os/BluetoothServiceManager.java b/android-34/android/os/BluetoothServiceManager.java
new file mode 100644
index 0000000..12f7bc8
--- /dev/null
+++ b/android-34/android/os/BluetoothServiceManager.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+import android.os.BluetoothServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the bluetooth
+ * service.
+ *
+ * @hide
+ */
+@SystemApi(client = Client.MODULE_LIBRARIES)
+public class BluetoothServiceManager {
+
+    /** @hide */
+    public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
+    /**
+     * @hide
+     */
+    public BluetoothServiceManager() {
+    }
+
+    /**
+     * A class that exposes the methods to register and obtain each system service.
+     */
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Register a system server binding object for a service.
+         */
+        public void register(@NonNull IBinder service) {
+            ServiceManager.addService(mServiceName, service);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it returns null.
+         */
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+         */
+        @NonNull
+        public IBinder getOrThrow() throws ServiceNotFoundException {
+            try {
+                return ServiceManager.getServiceOrThrow(mServiceName);
+            } catch (ServiceManager.ServiceNotFoundException e) {
+                throw new ServiceNotFoundException(mServiceName);
+            }
+        }
+
+        /**
+         * Get the system server binding object for a service. If the specified service is
+         * not available, it returns null.
+         */
+        @Nullable
+        public IBinder tryGet() {
+            return ServiceManager.checkService(mServiceName);
+        }
+    }
+
+    /**
+     * See {@link ServiceRegisterer#getOrThrow}.
+     *
+     */
+    public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+        /**
+         * Constructor.
+         *
+         * @param name the name of the binder service that cannot be found.
+         *
+         */
+        public ServiceNotFoundException(@NonNull String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "bluetooth" service.
+     */
+    @NonNull
+    public ServiceRegisterer getBluetoothManagerServiceRegisterer() {
+        return new ServiceRegisterer(BLUETOOTH_MANAGER_SERVICE);
+    }
+}
diff --git a/android-34/android/os/Broadcaster.java b/android-34/android/os/Broadcaster.java
new file mode 100644
index 0000000..88760b0
--- /dev/null
+++ b/android-34/android/os/Broadcaster.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+/** @hide */
+public class Broadcaster
+{
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public Broadcaster()
+    {
+    }
+
+    /**
+     *  Sign up for notifications about something.
+     *
+     *  When this broadcaster pushes a message with senderWhat in the what field,
+     *  target will be sent a copy of that message with targetWhat in the what field.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void request(int senderWhat, Handler target, int targetWhat)
+    {
+        synchronized (this) {
+            Registration r = null;
+            if (mReg == null) {
+                r = new Registration();
+                r.senderWhat = senderWhat;
+                r.targets = new Handler[1];
+                r.targetWhats = new int[1];
+                r.targets[0] = target;
+                r.targetWhats[0] = targetWhat;
+                mReg = r;
+                r.next = r;
+                r.prev = r;
+            } else {
+                // find its place in the map
+                Registration start = mReg;
+                r = start;
+                do {
+                    if (r.senderWhat >= senderWhat) {
+                        break;
+                    }
+                    r = r.next;
+                } while (r != start);
+                int n;
+                if (r.senderWhat != senderWhat) {
+                    // we didn't find a senderWhat match, but r is right
+                    // after where it goes
+                    Registration reg = new Registration();
+                    reg.senderWhat = senderWhat;
+                    reg.targets = new Handler[1];
+                    reg.targetWhats = new int[1];
+                    reg.next = r;
+                    reg.prev = r.prev;
+                    r.prev.next = reg;
+                    r.prev = reg;
+
+                    if (r == mReg && r.senderWhat > reg.senderWhat) {
+                        mReg = reg;
+                    }
+                    
+                    r = reg;
+                    n = 0;
+                } else {
+                    n = r.targets.length;
+                    Handler[] oldTargets = r.targets;
+                    int[] oldWhats = r.targetWhats;
+                    // check for duplicates, and don't do it if we are dup.
+                    for (int i=0; i<n; i++) {
+                        if (oldTargets[i] == target && oldWhats[i] == targetWhat) {
+                            return;
+                        }
+                    }
+                    r.targets = new Handler[n+1];
+                    System.arraycopy(oldTargets, 0, r.targets, 0, n);
+                    r.targetWhats = new int[n+1];
+                    System.arraycopy(oldWhats, 0, r.targetWhats, 0, n);
+                }
+                r.targets[n] = target;
+                r.targetWhats[n] = targetWhat;
+            }
+        }
+    }
+    
+    /**
+     * Unregister for notifications for this senderWhat/target/targetWhat tuple.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void cancelRequest(int senderWhat, Handler target, int targetWhat)
+    {
+        synchronized (this) {
+            Registration start = mReg;
+            Registration r = start;
+            
+            if (r == null) {
+                return;
+            }
+            
+            do {
+                if (r.senderWhat >= senderWhat) {
+                    break;
+                }
+                r = r.next;
+            } while (r != start);
+            
+            if (r.senderWhat == senderWhat) {
+                Handler[] targets = r.targets;
+                int[] whats = r.targetWhats;
+                int oldLen = targets.length;
+                for (int i=0; i<oldLen; i++) {
+                    if (targets[i] == target && whats[i] == targetWhat) {
+                        r.targets = new Handler[oldLen-1];
+                        r.targetWhats = new int[oldLen-1];
+                        if (i > 0) {
+                            System.arraycopy(targets, 0, r.targets, 0, i);
+                            System.arraycopy(whats, 0, r.targetWhats, 0, i);
+                        }
+
+                        int remainingLen = oldLen-i-1;
+                        if (remainingLen != 0) {
+                            System.arraycopy(targets, i+1, r.targets, i,
+                                    remainingLen);
+                            System.arraycopy(whats, i+1, r.targetWhats, i,
+                                    remainingLen);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * For debugging purposes, print the registrations to System.out
+     */
+    public void dumpRegistrations()
+    {
+        synchronized (this) {
+            Registration start = mReg;
+            System.out.println("Broadcaster " + this + " {");
+            if (start != null) {
+                Registration r = start;
+                do {
+                    System.out.println("    senderWhat=" + r.senderWhat);
+                    int n = r.targets.length;
+                    for (int i=0; i<n; i++) {
+                        System.out.println("        [" + r.targetWhats[i]
+                                        + "] " + r.targets[i]);
+                    }
+                    r = r.next;
+                } while (r != start);
+            }
+            System.out.println("}");
+        }
+    }
+
+    /**
+     * Send out msg.  Anyone who has registered via the request() method will be
+     * sent the message.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void broadcast(Message msg)
+    {
+        synchronized (this) {
+            if (mReg == null) {
+                return;
+            }
+            
+            int senderWhat = msg.what;
+            Registration start = mReg;
+            Registration r = start;
+            do {
+                if (r.senderWhat >= senderWhat) {
+                    break;
+                }
+                r = r.next;
+            } while (r != start);
+            if (r.senderWhat == senderWhat) {
+                Handler[] targets = r.targets;
+                int[] whats = r.targetWhats;
+                int n = targets.length;
+                for (int i=0; i<n; i++) {
+                    Handler target = targets[i];
+                    Message m = Message.obtain();
+                    m.copyFrom(msg);
+                    m.what = whats[i];
+                    target.sendMessage(m);
+                }
+            }
+        }
+    }
+
+    private class Registration
+    {
+        Registration next;
+        Registration prev;
+
+        int senderWhat;
+        Handler[] targets;
+        int[] targetWhats;
+    }
+    private Registration mReg;
+}
diff --git a/android-34/android/os/BugreportManager.java b/android-34/android/os/BugreportManager.java
new file mode 100644
index 0000000..086b0e5
--- /dev/null
+++ b/android-34/android/os/BugreportManager.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.WorkerThread;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.util.Preconditions;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Class that provides a privileged API to capture and consume bugreports.
+ *
+ * <p>This class may only be used by apps that currently have carrier privileges (see {@link
+ * android.telephony.TelephonyManager#hasCarrierPrivileges}) on an active SIM or priv-apps
+ * explicitly allowed by the device manufacturer.
+ *
+ * <p>Only one bugreport can be generated by the system at a time.
+ */
+@SystemService(Context.BUGREPORT_SERVICE)
+public final class BugreportManager {
+
+    private static final String TAG = "BugreportManager";
+
+    private final Context mContext;
+    private final IDumpstate mBinder;
+
+    /** @hide */
+    public BugreportManager(@NonNull Context context, IDumpstate binder) {
+        mContext = context;
+        mBinder = binder;
+    }
+
+    /**
+     * An interface describing the callback for bugreport progress and status.
+     *
+     * <p>Callers will receive {@link #onProgress} calls as the bugreport progresses, followed by a
+     * terminal call to either {@link #onFinished} or {@link #onError}.
+     *
+     * <p>If an issue is encountered while starting the bugreport asynchronously, callers will
+     * receive an {@link #onError} call without any {@link #onProgress} callbacks.
+     */
+    public abstract static class BugreportCallback {
+        /**
+         * Possible error codes taking a bugreport can encounter.
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(
+                prefix = {"BUGREPORT_ERROR_"},
+                value = {
+                    BUGREPORT_ERROR_INVALID_INPUT,
+                    BUGREPORT_ERROR_RUNTIME,
+                    BUGREPORT_ERROR_USER_DENIED_CONSENT,
+                    BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT,
+                    BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS,
+                    BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE
+                })
+        public @interface BugreportErrorCode {}
+
+        /**
+         * The input options were invalid. For example, the destination file the app provided could
+         * not be written by the system.
+         */
+        public static final int BUGREPORT_ERROR_INVALID_INPUT =
+                IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT;
+
+        /** A runtime error occurred. */
+        public static final int BUGREPORT_ERROR_RUNTIME =
+                IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR;
+
+        /** User denied consent to share the bugreport. */
+        public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT =
+                IDumpstateListener.BUGREPORT_ERROR_USER_DENIED_CONSENT;
+
+        /** The request to get user consent timed out. */
+        public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT =
+                IDumpstateListener.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT;
+
+        /** There is currently a bugreport running. The caller should try again later. */
+        public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS =
+                IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS;
+
+        /** There is no bugreport to retrieve for the caller. */
+        public static final int BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE =
+                IDumpstateListener.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE;
+
+        /**
+         * Called when there is a progress update.
+         *
+         * @param progress the progress in [0.0, 100.0]
+         */
+        public void onProgress(@FloatRange(from = 0f, to = 100f) float progress) {}
+
+        /**
+         * Called when taking bugreport resulted in an error.
+         *
+         * <p>If {@code BUGREPORT_ERROR_USER_DENIED_CONSENT} is passed, then the user did not
+         * consent to sharing the bugreport with the calling app.
+         *
+         * <p>If {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT} is passed, then the consent timed
+         * out, but the bugreport could be available in the internal directory of dumpstate for
+         * manual retrieval.
+         *
+         * <p>If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the caller
+         * should try later, as only one bugreport can be in progress at a time.
+         */
+        public void onError(@BugreportErrorCode int errorCode) {}
+
+        /** Called when taking bugreport finishes successfully.
+         *
+         * <p>This callback will be invoked if the
+         * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is not set.
+         */
+        public void onFinished() {}
+
+        /** Called when taking bugreport finishes successfully.
+         *
+         * <p>This callback will only be invoked if the
+         * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is set. Otherwise, the
+         * {@link #onFinished()} callback will be invoked.
+         *
+         * @param bugreportFile the absolute path of the generated bugreport file.
+         * @hide
+
+         */
+        @SystemApi
+        public void onFinished(@NonNull String bugreportFile) {}
+
+        /**
+         * Called when it is ready for calling app to show UI, showing any extra UI before this
+         * callback can interfere with bugreport generation.
+         */
+        public void onEarlyReportFinished() {}
+    }
+
+    /**
+     * Speculatively pre-dumps UI data for a bugreport request that might come later.
+     *
+     * <p>Triggers the dump of certain critical UI data, e.g. traces stored in short
+     * ring buffers that might get lost by the time the actual bugreport is requested.
+     *
+     * <p>{@link #startBugreport} will then pick the pre-dumped data if both of the following
+     * conditions are met:
+     * - {@link android.os.BugreportParams#BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA} is specified.
+     * - {@link #preDumpUiData} and {@link #startBugreport} were called by the same UID.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    @WorkerThread
+    public void preDumpUiData() {
+        try {
+            mBinder.preDumpUiData(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts a bugreport.
+     *
+     * <p>This starts a bugreport in the background. However the call itself can take several
+     * seconds to return in the worst case. {@code callback} will receive progress and status
+     * updates.
+     *
+     * <p>The bugreport artifacts will be copied over to the given file descriptors only if the user
+     * consents to sharing with the calling app. If
+     * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} is set, user consent will be deferred
+     * and no files will be copied to the given file descriptors.
+     *
+     * <p>{@link BugreportManager} takes ownership of {@code bugreportFd} and {@code screenshotFd}.
+     *
+     * @param bugreportFd file to write the bugreport. This should be opened in write-only, append
+     *     mode.
+     * @param screenshotFd file to write the screenshot, if necessary. This should be opened in
+     *     write-only, append mode.
+     * @param params options that specify what kind of a bugreport should be taken
+     * @param callback callback for progress and status updates
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.DUMP)
+    @WorkerThread
+    public void startBugreport(
+            @NonNull ParcelFileDescriptor bugreportFd,
+            @Nullable ParcelFileDescriptor screenshotFd,
+            @NonNull BugreportParams params,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BugreportCallback callback) {
+        try {
+            Preconditions.checkNotNull(bugreportFd);
+            Preconditions.checkNotNull(params);
+            Preconditions.checkNotNull(executor);
+            Preconditions.checkNotNull(callback);
+
+            boolean deferConsent =
+                    (params.getFlags() & BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT) != 0;
+            boolean isScreenshotRequested = screenshotFd != null || deferConsent;
+            if (screenshotFd == null) {
+                // Binder needs a valid File Descriptor to be passed
+                screenshotFd =
+                        ParcelFileDescriptor.open(
+                                new File("/dev/null"), ParcelFileDescriptor.MODE_READ_ONLY);
+            }
+            DumpstateListener dsListener =
+                    new DumpstateListener(executor, callback, isScreenshotRequested, deferConsent);
+            // Note: mBinder can get callingUid from the binder transaction.
+            mBinder.startBugreport(
+                    -1 /* callingUid */,
+                    mContext.getOpPackageName(),
+                    bugreportFd.getFileDescriptor(),
+                    screenshotFd.getFileDescriptor(),
+                    params.getMode(),
+                    params.getFlags(),
+                    dsListener,
+                    isScreenshotRequested);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (FileNotFoundException e) {
+            Log.wtf(TAG, "Not able to find /dev/null file: ", e);
+        } finally {
+            // We can close the file descriptors here because binder would have duped them.
+            IoUtils.closeQuietly(bugreportFd);
+            if (screenshotFd != null) {
+                IoUtils.closeQuietly(screenshotFd);
+            }
+        }
+    }
+
+    /**
+     * Retrieves a previously generated bugreport.
+     *
+     * <p>The previously generated bugreport must have been generated by calling {@link
+     * #startBugreport(ParcelFileDescriptor, ParcelFileDescriptor, BugreportParams,
+     * Executor, BugreportCallback)} with the {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT}
+     * flag set. The bugreport file returned by the {@link BugreportCallback#onFinished(String)}
+     * callback for a previously generated bugreport must be passed to this method. A caller may
+     * only retrieve bugreports that they have previously requested.
+     *
+     * <p>The bugreport artifacts will be copied over to the given file descriptor only if the user
+     * consents to sharing with the calling app.
+     *
+     * <p>{@link BugreportManager} takes ownership of {@code bugreportFd}.
+     *
+     * <p>The caller may only request to retrieve a given bugreport once. Subsequent calls will fail
+     * with error code {@link BugreportCallback#BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE}.
+     *
+     * @param bugreportFile the identifier for a bugreport that was previously generated for this
+     *      caller using {@code startBugreport}.
+     * @param bugreportFd file to copy over the previous bugreport. This should be opened in
+     *      write-only, append mode.
+     * @param executor the executor to execute callback methods.
+     * @param callback callback for progress and status updates.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.DUMP)
+    @WorkerThread
+    public void retrieveBugreport(
+            @NonNull String bugreportFile,
+            @NonNull ParcelFileDescriptor bugreportFd,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BugreportCallback callback
+    ) {
+        try {
+            Preconditions.checkNotNull(bugreportFile);
+            Preconditions.checkNotNull(bugreportFd);
+            Preconditions.checkNotNull(executor);
+            Preconditions.checkNotNull(callback);
+            DumpstateListener dsListener = new DumpstateListener(executor, callback, false, false);
+            mBinder.retrieveBugreport(Binder.getCallingUid(), mContext.getOpPackageName(),
+                    bugreportFd.getFileDescriptor(),
+                    bugreportFile,
+                    dsListener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } finally {
+            IoUtils.closeQuietly(bugreportFd);
+        }
+    }
+
+    /**
+     * Starts a connectivity bugreport.
+     *
+     * <p>The connectivity bugreport is a specialized version of bugreport that only includes
+     * information specifically for debugging connectivity-related issues (e.g. telephony, wi-fi,
+     * and IP networking issues). It is intended primarily for use by OEMs and network providers
+     * such as mobile network operators. In addition to generally excluding information that isn't
+     * targeted to connectivity debugging, this type of bugreport excludes PII and sensitive
+     * information that isn't strictly necessary for connectivity debugging.
+     *
+     * <p>The calling app MUST have a context-specific reason for requesting a connectivity
+     * bugreport, such as detecting a connectivity-related issue. This API SHALL NOT be used to
+     * perform random sampling from a fleet of public end-user devices.
+     *
+     * <p>Calling this API will cause the system to ask the user for consent every single time. The
+     * bugreport artifacts will be copied over to the given file descriptors only if the user
+     * consents to sharing with the calling app.
+     *
+     * <p>This starts a bugreport in the background. However the call itself can take several
+     * seconds to return in the worst case. {@code callback} will receive progress and status
+     * updates.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link
+     * android.telephony.TelephonyManager#hasCarrierPrivileges}) on any active subscription.
+     *
+     * @param bugreportFd file to write the bugreport. This should be opened in write-only, append
+     *     mode.
+     * @param callback callback for progress and status updates.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @WorkerThread
+    public void startConnectivityBugreport(
+            @NonNull ParcelFileDescriptor bugreportFd,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BugreportCallback callback) {
+        startBugreport(
+                bugreportFd,
+                null /* screenshotFd */,
+                new BugreportParams(BugreportParams.BUGREPORT_MODE_TELEPHONY),
+                executor,
+                callback);
+    }
+
+    /**
+     * Cancels the currently running bugreport.
+     *
+     * <p>Apps are only able to cancel their own bugreports. App A cannot cancel a bugreport started
+     * by app B.
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#DUMP} or that the calling app has
+     * carrier privileges (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on
+     * any active subscription.
+     *
+     * @throws SecurityException if trying to cancel another app's bugreport in progress
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @WorkerThread
+    public void cancelBugreport() {
+        try {
+            mBinder.cancelBugreport(-1 /* callingUid */, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests a bugreport.
+     *
+     * <p>This requests the platform/system to take a bugreport and makes the final bugreport
+     * available to the user. The user may choose to share it with another app, but the bugreport is
+     * never given back directly to the app that requested it.
+     *
+     * @param params {@link BugreportParams} that specify what kind of a bugreport should be taken,
+     *     please note that not all kinds of bugreport allow for a progress notification
+     * @param shareTitle title on the final share notification
+     * @param shareDescription description on the final share notification
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.DUMP)
+    public void requestBugreport(
+            @NonNull BugreportParams params,
+            @Nullable CharSequence shareTitle,
+            @Nullable CharSequence shareDescription) {
+        try {
+            String title = shareTitle == null ? null : shareTitle.toString();
+            String description = shareDescription == null ? null : shareDescription.toString();
+            ActivityManager.getService()
+                    .requestBugReportWithDescription(title, description, params.getMode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private final class DumpstateListener extends IDumpstateListener.Stub {
+        private final Executor mExecutor;
+        private final BugreportCallback mCallback;
+        private final boolean mIsScreenshotRequested;
+        private final boolean mIsConsentDeferred;
+
+        DumpstateListener(
+                Executor executor, BugreportCallback callback, boolean isScreenshotRequested,
+                boolean isConsentDeferred) {
+            mExecutor = executor;
+            mCallback = callback;
+            mIsScreenshotRequested = isScreenshotRequested;
+            mIsConsentDeferred = isConsentDeferred;
+        }
+
+        @Override
+        public void onProgress(int progress) throws RemoteException {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onProgress(progress));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void onError(int errorCode) throws RemoteException {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onError(errorCode));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void onFinished(String bugreportFile) throws RemoteException {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (mIsConsentDeferred) {
+                    mExecutor.execute(() -> mCallback.onFinished(bugreportFile));
+                } else {
+                    mExecutor.execute(() -> mCallback.onFinished());
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void onScreenshotTaken(boolean success) throws RemoteException {
+            if (!mIsScreenshotRequested) {
+                return;
+            }
+
+            Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+            mainThreadHandler.post(
+                    () -> {
+                        int message =
+                                success
+                                        ? R.string.bugreport_screenshot_success_toast
+                                        : R.string.bugreport_screenshot_failure_toast;
+                        Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+                    });
+        }
+
+        @Override
+        public void onUiIntensiveBugreportDumpsFinished() throws RemoteException {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onEarlyReportFinished());
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/BugreportParams.java b/android-34/android/os/BugreportParams.java
new file mode 100644
index 0000000..d9d14b0
--- /dev/null
+++ b/android-34/android/os/BugreportParams.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Parameters that specify what kind of bugreport should be taken.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BugreportParams {
+    private final int mMode;
+    private final int mFlags;
+
+    /**
+     * Constructs a BugreportParams object to specify what kind of bugreport should be taken.
+     *
+     * @param mode of the bugreport to request
+     */
+    public BugreportParams(@BugreportMode int mode) {
+        mMode = mode;
+        mFlags = 0;
+    }
+
+    /**
+     * Constructs a BugreportParams object to specify what kind of bugreport should be taken.
+     *
+     * @param mode of the bugreport to request
+     * @param flags to customize the bugreport request
+     */
+    public BugreportParams(@BugreportMode int mode, @BugreportFlag int flags) {
+        mMode = mode;
+        mFlags = flags;
+    }
+
+    /**
+     * Returns the mode of the bugreport to request.
+     */
+    @BugreportMode
+    public int getMode() {
+        return mMode;
+    }
+
+    /**
+     * Returns the flags to customize the bugreport request.
+     */
+    @BugreportFlag
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Defines acceptable types of bugreports.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "BUGREPORT_MODE_" }, value = {
+            BUGREPORT_MODE_FULL,
+            BUGREPORT_MODE_INTERACTIVE,
+            BUGREPORT_MODE_REMOTE,
+            BUGREPORT_MODE_WEAR,
+            BUGREPORT_MODE_TELEPHONY,
+            BUGREPORT_MODE_WIFI
+    })
+    public @interface BugreportMode {}
+
+    /**
+     * Options for a bugreport without user interference (and hence causing less
+     * interference to the system), but includes all sections.
+     */
+    public static final int BUGREPORT_MODE_FULL = IDumpstate.BUGREPORT_MODE_FULL;
+
+    /**
+     * Options that allow user to monitor progress and enter additional data; might not
+     * include all sections.
+     */
+    public static final int BUGREPORT_MODE_INTERACTIVE = IDumpstate.BUGREPORT_MODE_INTERACTIVE;
+
+    /**
+     * Options for a bugreport requested remotely by administrator of the Device Owner app,
+     * not the device's user.
+     */
+    public static final int BUGREPORT_MODE_REMOTE = IDumpstate.BUGREPORT_MODE_REMOTE;
+
+    /**
+     * Options for a bugreport on a wearable device.
+     */
+    public static final int BUGREPORT_MODE_WEAR = IDumpstate.BUGREPORT_MODE_WEAR;
+
+    /**
+     * Options for a lightweight version of bugreport that only includes a few, urgent
+     * sections used to report telephony bugs.
+     */
+    public static final int BUGREPORT_MODE_TELEPHONY = IDumpstate.BUGREPORT_MODE_TELEPHONY;
+
+    /**
+     * Options for a lightweight bugreport that only includes a few sections related to
+     * Wifi.
+     */
+    public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI;
+
+    /**
+     * Defines acceptable flags for customizing bugreport requests.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "BUGREPORT_FLAG_" }, value = {
+            BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA,
+            BUGREPORT_FLAG_DEFER_CONSENT
+    })
+    public @interface BugreportFlag {}
+
+    /**
+     * Flag for reusing pre-dumped UI data. The pre-dump and bugreport request calls must be
+     * performed by the same UID, otherwise the flag is ignored.
+     */
+    public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA =
+            IDumpstate.BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA;
+
+    /**
+     * Flag for deferring user consent.
+     *
+     * <p>This flag should be used in cases where it may not be possible for the user to respond
+     * to a consent dialog immediately, such as when the user is driving. The generated bugreport
+     * may be retrieved at a later time using {@link BugreportManager#retrieveBugreport(
+     * String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}.
+     */
+    public static final int BUGREPORT_FLAG_DEFER_CONSENT = IDumpstate.BUGREPORT_FLAG_DEFER_CONSENT;
+}
diff --git a/android-34/android/os/Build.java b/android-34/android/os/Build.java
new file mode 100644
index 0000000..9f9c222
--- /dev/null
+++ b/android-34/android/os/Build.java
@@ -0,0 +1,1584 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.app.Application;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.sysprop.DeviceProperties;
+import android.sysprop.SocProperties;
+import android.sysprop.TelephonyProperties;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.View;
+
+import dalvik.system.VMRuntime;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Information about the current build, extracted from system properties.
+ */
+public class Build {
+    private static final String TAG = "Build";
+
+    /** Value used for when a build property is unknown. */
+    public static final String UNKNOWN = "unknown";
+
+    /** Either a changelist number, or a label like "M4-rc20". */
+    public static final String ID = getString("ro.build.id");
+
+    /** A build ID string meant for displaying to the user */
+    public static final String DISPLAY = getString("ro.build.display.id");
+
+    /** The name of the overall product. */
+    public static final String PRODUCT = getString("ro.product.name");
+
+    /**
+     * The product name for attestation. In non-default builds (like the AOSP build) the value of
+     * the 'PRODUCT' system property may be different to the one provisioned to KeyMint,
+     * and Keymint attestation would still attest to the product name which was provisioned.
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public static final String PRODUCT_FOR_ATTESTATION = getVendorDeviceIdProperty("name");
+
+    /** The name of the industrial design. */
+    public static final String DEVICE = getString("ro.product.device");
+
+    /**
+     * The device name for attestation. In non-default builds (like the AOSP build) the value of
+     * the 'DEVICE' system property may be different to the one provisioned to KeyMint,
+     * and Keymint attestation would still attest to the device name which was provisioned.
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public static final String DEVICE_FOR_ATTESTATION =
+            getVendorDeviceIdProperty("device");
+
+    /** The name of the underlying board, like "goldfish". */
+    public static final String BOARD = getString("ro.product.board");
+
+    /**
+     * The name of the instruction set (CPU type + ABI convention) of native code.
+     *
+     * @deprecated Use {@link #SUPPORTED_ABIS} instead.
+     */
+    @Deprecated
+    public static final String CPU_ABI;
+
+    /**
+     * The name of the second instruction set (CPU type + ABI convention) of native code.
+     *
+     * @deprecated Use {@link #SUPPORTED_ABIS} instead.
+     */
+    @Deprecated
+    public static final String CPU_ABI2;
+
+    /** The manufacturer of the product/hardware. */
+    public static final String MANUFACTURER = getString("ro.product.manufacturer");
+
+    /**
+     * The manufacturer name for attestation. In non-default builds (like the AOSP build) the value
+     * of the 'MANUFACTURER' system property may be different to the one provisioned to KeyMint,
+     * and Keymint attestation would still attest to the manufacturer which was provisioned.
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public static final String MANUFACTURER_FOR_ATTESTATION =
+            getVendorDeviceIdProperty("manufacturer");
+
+    /** The consumer-visible brand with which the product/hardware will be associated, if any. */
+    public static final String BRAND = getString("ro.product.brand");
+
+    /**
+     * The product brand for attestation. In non-default builds (like the AOSP build) the value of
+     * the 'BRAND' system property may be different to the one provisioned to KeyMint,
+     * and Keymint attestation would still attest to the product brand which was provisioned.
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public static final String BRAND_FOR_ATTESTATION = getVendorDeviceIdProperty("brand");
+
+    /** The end-user-visible name for the end product. */
+    public static final String MODEL = getString("ro.product.model");
+
+    /**
+     * The product model for attestation. In non-default builds (like the AOSP build) the value of
+     * the 'MODEL' system property may be different to the one provisioned to KeyMint,
+     * and Keymint attestation would still attest to the product model which was provisioned.
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public static final String MODEL_FOR_ATTESTATION = getVendorDeviceIdProperty("model");
+
+    /** The manufacturer of the device's primary system-on-chip. */
+    @NonNull
+    public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN);
+
+    /** The model name of the device's primary system-on-chip. */
+    @NonNull
+    public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN);
+
+    /** The system bootloader version number. */
+    public static final String BOOTLOADER = getString("ro.bootloader");
+
+    /**
+     * The radio firmware version number.
+     *
+     * @deprecated The radio firmware version is frequently not
+     * available when this class is initialized, leading to a blank or
+     * "unknown" value for this string.  Use
+     * {@link #getRadioVersion} instead.
+     */
+    @Deprecated
+    public static final String RADIO = joinListOrElse(
+            TelephonyProperties.baseband_version(), UNKNOWN);
+
+    /** The name of the hardware (from the kernel command line or /proc). */
+    public static final String HARDWARE = getString("ro.hardware");
+
+    /**
+     * The SKU of the hardware (from the kernel command line).
+     *
+     * <p>The SKU is reported by the bootloader to configure system software features.
+     * If no value is supplied by the bootloader, this is reported as {@link #UNKNOWN}.
+
+     */
+    @NonNull
+    public static final String SKU = getString("ro.boot.hardware.sku");
+
+    /**
+     * The SKU of the device as set by the original design manufacturer (ODM).
+     *
+     * <p>This is a runtime-initialized property set during startup to configure device
+     * services. If no value is set, this is reported as {@link #UNKNOWN}.
+     *
+     * <p>The ODM SKU may have multiple variants for the same system SKU in case a manufacturer
+     * produces variants of the same design. For example, the same build may be released with
+     * variations in physical keyboard and/or display hardware, each with a different ODM SKU.
+     */
+    @NonNull
+    public static final String ODM_SKU = getString("ro.boot.product.hardware.sku");
+
+    /**
+     * Whether this build was for an emulator device.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final boolean IS_EMULATOR = getString("ro.boot.qemu").equals("1");
+
+    /**
+     * A hardware serial number, if available. Alphanumeric only, case-insensitive.
+     * This field is always set to {@link Build#UNKNOWN}.
+     *
+     * @deprecated Use {@link #getSerial()} instead.
+     **/
+    @Deprecated
+    // IMPORTANT: This field should be initialized via a function call to
+    // prevent its value being inlined in the app during compilation because
+    // we will later set it to the value based on the app's target SDK.
+    public static final String SERIAL = getString("no.such.thing");
+
+    /**
+     * Gets the hardware serial number, if available.
+     *
+     * <p class="note"><b>Note:</b> Root access may allow you to modify device identifiers, such as
+     * the hardware serial number. If you change these identifiers, you can not use
+     * <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain
+     * proof of the device's original identifiers. KeyMint will reject an ID attestation request
+     * if the identifiers provided by the frameworks do not match the identifiers it was
+     * provisioned with.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app has carrier privileges (see {@link
+     *     android.telephony.TelephonyManager#hasCarrierPrivileges}) on any active subscription.
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     android.app.role.RoleManager#isRoleHeld(String)}).
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then {@link Build#UNKNOWN} is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @return The serial number if specified.
+     */
+    @SuppressAutoDoc // No support for device / profile owner.
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static String getSerial() {
+        IDeviceIdentifiersPolicyService service = IDeviceIdentifiersPolicyService.Stub
+                .asInterface(ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE));
+        try {
+            Application application = ActivityThread.currentApplication();
+            String callingPackage = application != null ? application.getPackageName() : null;
+            return service.getSerialForPackage(callingPackage, null);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return UNKNOWN;
+    }
+
+    /**
+     * An ordered list of ABIs supported by this device. The most preferred ABI is the first
+     * element in the list.
+     *
+     * See {@link #SUPPORTED_32_BIT_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}.
+     */
+    public static final String[] SUPPORTED_ABIS = getStringList("ro.product.cpu.abilist", ",");
+
+    /**
+     * An ordered list of <b>32 bit</b> ABIs supported by this device. The most preferred ABI
+     * is the first element in the list.
+     *
+     * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}.
+     */
+    public static final String[] SUPPORTED_32_BIT_ABIS =
+            getStringList("ro.product.cpu.abilist32", ",");
+
+    /**
+     * An ordered list of <b>64 bit</b> ABIs supported by this device. The most preferred ABI
+     * is the first element in the list.
+     *
+     * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_32_BIT_ABIS}.
+     */
+    public static final String[] SUPPORTED_64_BIT_ABIS =
+            getStringList("ro.product.cpu.abilist64", ",");
+
+    /** {@hide} */
+    @TestApi
+    public static boolean is64BitAbi(String abi) {
+        return VMRuntime.is64BitAbi(abi);
+    }
+
+    static {
+        /*
+         * Adjusts CPU_ABI and CPU_ABI2 depending on whether or not a given process is 64 bit.
+         * 32 bit processes will always see 32 bit ABIs in these fields for backward
+         * compatibility.
+         */
+        final String[] abiList;
+        if (VMRuntime.getRuntime().is64Bit()) {
+            abiList = SUPPORTED_64_BIT_ABIS;
+        } else {
+            abiList = SUPPORTED_32_BIT_ABIS;
+        }
+
+        CPU_ABI = abiList[0];
+        if (abiList.length > 1) {
+            CPU_ABI2 = abiList[1];
+        } else {
+            CPU_ABI2 = "";
+        }
+    }
+
+    /** Various version strings. */
+    public static class VERSION {
+        /**
+         * The internal value used by the underlying source control to
+         * represent this build.  E.g., a perforce changelist number
+         * or a git hash.
+         */
+        public static final String INCREMENTAL = getString("ro.build.version.incremental");
+
+        /**
+         * The user-visible version string.  E.g., "1.0" or "3.4b5" or "bananas".
+         *
+         * This field is an opaque string. Do not assume that its value
+         * has any particular structure or that values of RELEASE from
+         * different releases can be somehow ordered.
+         */
+        public static final String RELEASE = getString("ro.build.version.release");
+
+        /**
+         * The version string.  May be {@link #RELEASE} or {@link #CODENAME} if
+         * not a final release build.
+         */
+        @NonNull public static final String RELEASE_OR_CODENAME = getString(
+                "ro.build.version.release_or_codename");
+
+        /**
+         * The version string we show to the user; may be {@link #RELEASE} or
+         * a descriptive string if not a final release build.
+         */
+        @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY = getString(
+                "ro.build.version.release_or_preview_display");
+
+        /**
+         * The base OS build the product is based on.
+         */
+        public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
+
+        /**
+         * The user-visible security patch level. This value represents the date when the device
+         * most recently applied a security patch.
+         */
+        public static final String SECURITY_PATCH = SystemProperties.get(
+                "ro.build.version.security_patch", "");
+
+        /**
+         * The media performance class of the device or 0 if none.
+         * <p>
+         * If this value is not <code>0</code>, the device conforms to the media performance class
+         * definition of the SDK version of this value. This value never changes while a device is
+         * booted, but it may increase when the hardware manufacturer provides an OTA update.
+         * <p>
+         * Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
+         * {@link Build.VERSION_CODES#R}.
+         */
+        public static final int MEDIA_PERFORMANCE_CLASS =
+                DeviceProperties.media_performance_class().orElse(0);
+
+        /**
+         * The user-visible SDK version of the framework in its raw String
+         * representation; use {@link #SDK_INT} instead.
+         *
+         * @deprecated Use {@link #SDK_INT} to easily get this as an integer.
+         */
+        @Deprecated
+        public static final String SDK = getString("ro.build.version.sdk");
+
+        /**
+         * The SDK version of the software currently running on this hardware
+         * device. This value never changes while a device is booted, but it may
+         * increase when the hardware manufacturer provides an OTA update.
+         * <p>
+         * Possible values are defined in {@link Build.VERSION_CODES}.
+         */
+        public static final int SDK_INT = SystemProperties.getInt(
+                "ro.build.version.sdk", 0);
+
+        /**
+         * The SDK version of the software that <em>initially</em> shipped on
+         * this hardware device. It <em>never</em> changes during the lifetime
+         * of the device, even when {@link #SDK_INT} increases due to an OTA
+         * update.
+         * <p>
+         * Possible values are defined in {@link Build.VERSION_CODES}.
+         *
+         * @see #SDK_INT
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @TestApi
+        public static final int DEVICE_INITIAL_SDK_INT = SystemProperties
+                .getInt("ro.product.first_api_level", 0);
+
+        /**
+         * The developer preview revision of a prerelease SDK. This value will always
+         * be <code>0</code> on production platform builds/devices.
+         *
+         * <p>When this value is nonzero, any new API added since the last
+         * officially published {@link #SDK_INT API level} is only guaranteed to be present
+         * on that specific preview revision. For example, an API <code>Activity.fooBar()</code>
+         * might be present in preview revision 1 but renamed or removed entirely in
+         * preview revision 2, which may cause an app attempting to call it to crash
+         * at runtime.</p>
+         *
+         * <p>Experimental apps targeting preview APIs should check this value for
+         * equality (<code>==</code>) with the preview SDK revision they were built for
+         * before using any prerelease platform APIs. Apps that detect a preview SDK revision
+         * other than the specific one they expect should fall back to using APIs from
+         * the previously published API level only to avoid unwanted runtime exceptions.
+         * </p>
+         */
+        public static final int PREVIEW_SDK_INT = SystemProperties.getInt(
+                "ro.build.version.preview_sdk", 0);
+
+        /**
+         * The SDK fingerprint for a given prerelease SDK. This value will always be
+         * {@code REL} on production platform builds/devices.
+         *
+         * <p>When this value is not {@code REL}, it contains a string fingerprint of the API
+         * surface exposed by the preview SDK. Preview platforms with different API surfaces
+         * will have different {@code PREVIEW_SDK_FINGERPRINT}.
+         *
+         * <p>This attribute is intended for use by installers for finer grained targeting of
+         * packages. Applications targeting preview APIs should not use this field and should
+         * instead use {@code PREVIEW_SDK_INT} or use reflection or other runtime checks to
+         * detect the presence of an API or guard themselves against unexpected runtime
+         * behavior.
+         *
+         * @hide
+         */
+        @SystemApi
+        @NonNull public static final String PREVIEW_SDK_FINGERPRINT = SystemProperties.get(
+                "ro.build.version.preview_sdk_fingerprint", "REL");
+
+        /**
+         * The current development codename, or the string "REL" if this is
+         * a release build.
+         */
+        public static final String CODENAME = getString("ro.build.version.codename");
+
+        /**
+         * All known codenames that are present in {@link VERSION_CODES}.
+         *
+         * <p>This includes in development codenames as well, i.e. if {@link #CODENAME} is not "REL"
+         * then the value of that is present in this set.
+         *
+         * <p>If a particular string is not present in this set, then it is either not a codename
+         * or a codename for a future release. For example, during Android R development, "Tiramisu"
+         * was not a known codename.
+         *
+         * @hide
+         */
+        @SystemApi
+        @NonNull public static final Set<String> KNOWN_CODENAMES =
+                new ArraySet<>(getStringList("ro.build.version.known_codenames", ","));
+
+        private static final String[] ALL_CODENAMES
+                = getStringList("ro.build.version.all_codenames", ",");
+
+        /**
+         * @hide
+         */
+        @UnsupportedAppUsage
+        @TestApi
+        public static final String[] ACTIVE_CODENAMES = "REL".equals(ALL_CODENAMES[0])
+                ? new String[0] : ALL_CODENAMES;
+
+        /**
+         * The SDK version to use when accessing resources.
+         * Use the current SDK version code.  For every active development codename
+         * we are operating under, we bump the assumed resource platform version by 1.
+         * @hide
+         */
+        @TestApi
+        public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length;
+
+        /**
+         * The current lowest supported value of app target SDK. Applications targeting
+         * lower values may not function on devices running this SDK version. Its possible
+         * values are defined in {@link Build.VERSION_CODES}.
+         *
+         * @hide
+         */
+        public static final int MIN_SUPPORTED_TARGET_SDK_INT = SystemProperties.getInt(
+                "ro.build.version.min_supported_target_sdk", 0);
+    }
+
+    /**
+     * Enumeration of the currently known SDK version codes.  These are the
+     * values that can be found in {@link VERSION#SDK}.  Version numbers
+     * increment monotonically with each official platform release.
+     */
+    public static class VERSION_CODES {
+        /**
+         * Magic version number for a current development build, which has
+         * not yet turned into an official release.
+         */
+        // This must match VMRuntime.SDK_VERSION_CUR_DEVELOPMENT.
+        public static final int CUR_DEVELOPMENT = 10000;
+
+        /**
+         * The original, first, version of Android.  Yay!
+         *
+         * <p>Released publicly as Android 1.0 in September 2008.
+         */
+        public static final int BASE = 1;
+
+        /**
+         * First Android update.
+         *
+         * <p>Released publicly as Android 1.1 in February 2009.
+         */
+        public static final int BASE_1_1 = 2;
+
+        /**
+         * C.
+         *
+         * <p>Released publicly as Android 1.5 in April 2009.
+         */
+        public static final int CUPCAKE = 3;
+
+        /**
+         * D.
+         *
+         * <p>Released publicly as Android 1.6 in September 2009.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> They must explicitly request the
+         * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission to be
+         * able to modify the contents of the SD card.  (Apps targeting
+         * earlier versions will always request the permission.)
+         * <li> They must explicitly request the
+         * {@link android.Manifest.permission#READ_PHONE_STATE} permission to be
+         * able to be able to retrieve phone state info.  (Apps targeting
+         * earlier versions will always request the permission.)
+         * <li> They are assumed to support different screen densities and
+         * sizes.  (Apps targeting earlier versions are assumed to only support
+         * medium density normal size screens unless otherwise indicated).
+         * They can still explicitly specify screen support either way with the
+         * supports-screens manifest tag.
+         * <li> {@link android.widget.TabHost} will use the new dark tab
+         * background design.
+         * </ul>
+         */
+        public static final int DONUT = 4;
+
+        /**
+         * E.
+         *
+         * <p>Released publicly as Android 2.0 in October 2009.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> The {@link android.app.Service#onStartCommand
+         * Service.onStartCommand} function will return the new
+         * {@link android.app.Service#START_STICKY} behavior instead of the
+         * old compatibility {@link android.app.Service#START_STICKY_COMPATIBILITY}.
+         * <li> The {@link android.app.Activity} class will now execute back
+         * key presses on the key up instead of key down, to be able to detect
+         * canceled presses from virtual keys.
+         * <li> The {@link android.widget.TabWidget} class will use a new color scheme
+         * for tabs. In the new scheme, the foreground tab has a medium gray background
+         * the background tabs have a dark gray background.
+         * </ul>
+         */
+        public static final int ECLAIR = 5;
+
+        /**
+         * E incremental update.
+         *
+         * <p>Released publicly as Android 2.0.1 in December 2009.
+         */
+        public static final int ECLAIR_0_1 = 6;
+
+        /**
+         * E MR1.
+         *
+         * <p>Released publicly as Android 2.1 in January 2010.
+         */
+        public static final int ECLAIR_MR1 = 7;
+
+        /**
+         * F.
+         *
+         * <p>Released publicly as Android 2.2 in May 2010.
+         */
+        public static final int FROYO = 8;
+
+        /**
+         * G.
+         *
+         * <p>Released publicly as Android 2.3 in December 2010.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> The application's notification icons will be shown on the new
+         * dark status bar background, so must be visible in this situation.
+         * </ul>
+         */
+        public static final int GINGERBREAD = 9;
+
+        /**
+         * G MR1.
+         *
+         * <p>Released publicly as Android 2.3.3 in February 2011.
+         */
+        public static final int GINGERBREAD_MR1 = 10;
+
+        /**
+         * H.
+         *
+         * <p>Released publicly as Android 3.0 in February 2011.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> The default theme for applications is now dark holographic:
+         *      {@link android.R.style#Theme_Holo}.
+         * <li> On large screen devices that do not have a physical menu
+         * button, the soft (compatibility) menu is disabled.
+         * <li> The activity lifecycle has changed slightly as per
+         * {@link android.app.Activity}.
+         * <li> An application will crash if it does not call through
+         * to the super implementation of its
+         * {@link android.app.Activity#onPause Activity.onPause()} method.
+         * <li> When an application requires a permission to access one of
+         * its components (activity, receiver, service, provider), this
+         * permission is no longer enforced when the application wants to
+         * access its own component.  This means it can require a permission
+         * on a component that it does not itself hold and still access that
+         * component.
+         * <li> {@link android.content.Context#getSharedPreferences
+         * Context.getSharedPreferences()} will not automatically reload
+         * the preferences if they have changed on storage, unless
+         * {@link android.content.Context#MODE_MULTI_PROCESS} is used.
+         * <li> {@link android.view.ViewGroup#setMotionEventSplittingEnabled}
+         * will default to true.
+         * <li> {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH}
+         * is enabled by default on windows.
+         * <li> {@link android.widget.PopupWindow#isSplitTouchEnabled()
+         * PopupWindow.isSplitTouchEnabled()} will return true by default.
+         * <li> {@link android.widget.GridView} and {@link android.widget.ListView}
+         * will use {@link android.view.View#setActivated View.setActivated}
+         * for selected items if they do not implement {@link android.widget.Checkable}.
+         * <li> {@link android.widget.Scroller} will be constructed with
+         * "flywheel" behavior enabled by default.
+         * </ul>
+         */
+        public static final int HONEYCOMB = 11;
+
+        /**
+         * H MR1.
+         *
+         * <p>Released publicly as Android 3.1 in May 2011.
+         */
+        public static final int HONEYCOMB_MR1 = 12;
+
+        /**
+         * H MR2.
+         *
+         * <p>Released publicly as Android 3.2 in July 2011.
+         * <p>Update to Honeycomb MR1 to support 7 inch tablets, improve
+         * screen compatibility mode, etc.</p>
+         *
+         * <p>As of this version, applications that don't say whether they
+         * support XLARGE screens will be assumed to do so only if they target
+         * {@link #HONEYCOMB} or later; it had been {@link #GINGERBREAD} or
+         * later.  Applications that don't support a screen size at least as
+         * large as the current screen will provide the user with a UI to
+         * switch them in to screen size compatibility mode.</p>
+         *
+         * <p>This version introduces new screen size resource qualifiers
+         * based on the screen size in dp: see
+         * {@link android.content.res.Configuration#screenWidthDp},
+         * {@link android.content.res.Configuration#screenHeightDp}, and
+         * {@link android.content.res.Configuration#smallestScreenWidthDp}.
+         * Supplying these in &lt;supports-screens&gt; as per
+         * {@link android.content.pm.ApplicationInfo#requiresSmallestWidthDp},
+         * {@link android.content.pm.ApplicationInfo#compatibleWidthLimitDp}, and
+         * {@link android.content.pm.ApplicationInfo#largestWidthLimitDp} is
+         * preferred over the older screen size buckets and for older devices
+         * the appropriate buckets will be inferred from them.</p>
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li><p>New {@link android.content.pm.PackageManager#FEATURE_SCREEN_PORTRAIT}
+         * and {@link android.content.pm.PackageManager#FEATURE_SCREEN_LANDSCAPE}
+         * features were introduced in this release.  Applications that target
+         * previous platform versions are assumed to require both portrait and
+         * landscape support in the device; when targeting Honeycomb MR1 or
+         * greater the application is responsible for specifying any specific
+         * orientation it requires.</p>
+         * <li><p>{@link android.os.AsyncTask} will use the serial executor
+         * by default when calling {@link android.os.AsyncTask#execute}.</p>
+         * <li><p>{@link android.content.pm.ActivityInfo#configChanges
+         * ActivityInfo.configChanges} will have the
+         * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_SIZE} and
+         * {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE}
+         * bits set; these need to be cleared for older applications because
+         * some developers have done absolute comparisons against this value
+         * instead of correctly masking the bits they are interested in.
+         * </ul>
+         */
+        public static final int HONEYCOMB_MR2 = 13;
+
+        /**
+         * I.
+         *
+         * <p>Released publicly as Android 4.0 in October 2011.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> For devices without a dedicated menu key, the software compatibility
+         * menu key will not be shown even on phones.  By targeting Ice Cream Sandwich
+         * or later, your UI must always have its own menu UI affordance if needed,
+         * on both tablets and phones.  The ActionBar will take care of this for you.
+         * <li> 2d drawing hardware acceleration is now turned on by default.
+         * You can use
+         * {@link android.R.attr#hardwareAccelerated android:hardwareAccelerated}
+         * to turn it off if needed, although this is strongly discouraged since
+         * it will result in poor performance on larger screen devices.
+         * <li> The default theme for applications is now the "device default" theme:
+         *      {@link android.R.style#Theme_DeviceDefault}. This may be the
+         *      holo dark theme or a different dark theme defined by the specific device.
+         *      The {@link android.R.style#Theme_Holo} family must not be modified
+         *      for a device to be considered compatible. Applications that explicitly
+         *      request a theme from the Holo family will be guaranteed that these themes
+         *      will not change character within the same platform version. Applications
+         *      that wish to blend in with the device should use a theme from the
+         *      {@link android.R.style#Theme_DeviceDefault} family.
+         * <li> Managed cursors can now throw an exception if you directly close
+         * the cursor yourself without stopping the management of it; previously failures
+         * would be silently ignored.
+         * <li> The fadingEdge attribute on views will be ignored (fading edges is no
+         * longer a standard part of the UI).  A new requiresFadingEdge attribute allows
+         * applications to still force fading edges on for special cases.
+         * <li> {@link android.content.Context#bindService Context.bindService()}
+         * will not automatically add in {@link android.content.Context#BIND_WAIVE_PRIORITY}.
+         * <li> App Widgets will have standard padding automatically added around
+         * them, rather than relying on the padding being baked into the widget itself.
+         * <li> An exception will be thrown if you try to change the type of a
+         * window after it has been added to the window manager.  Previously this
+         * would result in random incorrect behavior.
+         * <li> {@link android.view.animation.AnimationSet} will parse out
+         * the duration, fillBefore, fillAfter, repeatMode, and startOffset
+         * XML attributes that are defined.
+         * <li> {@link android.app.ActionBar#setHomeButtonEnabled
+         * ActionBar.setHomeButtonEnabled()} is false by default.
+         * </ul>
+         */
+        public static final int ICE_CREAM_SANDWICH = 14;
+
+        /**
+         * I MR1.
+         *
+         * <p>Released publicly as Android 4.03 in December 2011.
+         */
+        public static final int ICE_CREAM_SANDWICH_MR1 = 15;
+
+        /**
+         * J.
+         *
+         * <p>Released publicly as Android 4.1 in July 2012.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> You must explicitly request the {@link android.Manifest.permission#READ_CALL_LOG}
+         * and/or {@link android.Manifest.permission#WRITE_CALL_LOG} permissions;
+         * access to the call log is no longer implicitly provided through
+         * {@link android.Manifest.permission#READ_CONTACTS} and
+         * {@link android.Manifest.permission#WRITE_CONTACTS}.
+         * <li> {@link android.widget.RemoteViews} will throw an exception if
+         * setting an onClick handler for views being generated by a
+         * {@link android.widget.RemoteViewsService} for a collection container;
+         * previously this just resulted in a warning log message.
+         * <li> New {@link android.app.ActionBar} policy for embedded tabs:
+         * embedded tabs are now always stacked in the action bar when in portrait
+         * mode, regardless of the size of the screen.
+         * <li> {@link android.webkit.WebSettings#setAllowFileAccessFromFileURLs(boolean)
+         * WebSettings.setAllowFileAccessFromFileURLs} and
+         * {@link android.webkit.WebSettings#setAllowUniversalAccessFromFileURLs(boolean)
+         * WebSettings.setAllowUniversalAccessFromFileURLs} default to false.
+         * <li> Calls to {@link android.content.pm.PackageManager#setComponentEnabledSetting
+         * PackageManager.setComponentEnabledSetting} will now throw an
+         * IllegalArgumentException if the given component class name does not
+         * exist in the application's manifest.
+         * <li> {@code NfcAdapter.setNdefPushMessage},
+         * {@code NfcAdapter.setNdefPushMessageCallback} and
+         * {@code NfcAdapter.setOnNdefPushCompleteCallback} will throw
+         * IllegalStateException if called after the Activity has been destroyed.
+         * <li> Accessibility services must require the new
+         * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission or
+         * they will not be available for use.
+         * <li> {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+         * AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} must be set
+         * for unimportant views to be included in queries.
+         * </ul>
+         */
+        public static final int JELLY_BEAN = 16;
+
+        /**
+         * J MR1.
+         *
+         * <p>Released publicly as Android 4.2 in November 2012.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li>Content Providers: The default value of {@code android:exported} is now
+         * {@code false}. See
+         * <a href="{@docRoot}guide/topics/manifest/provider-element.html#exported">
+         * the android:exported section</a> in the provider documentation for more details.</li>
+         * <li>{@link android.view.View#getLayoutDirection() View.getLayoutDirection()}
+         * can return different values than {@link android.view.View#LAYOUT_DIRECTION_LTR}
+         * based on the locale etc.
+         * <li> {@link android.webkit.WebView#addJavascriptInterface(Object, String)
+         * WebView.addJavascriptInterface} requires explicit annotations on methods
+         * for them to be accessible from Javascript.
+         * </ul>
+         */
+        public static final int JELLY_BEAN_MR1 = 17;
+
+        /**
+         * J MR2.
+         *
+         * <p>Released publicly as Android 4.3 in July 2013.
+         */
+        public static final int JELLY_BEAN_MR2 = 18;
+
+        /**
+         * K.
+         *
+         * <p>Released publicly as Android 4.4 in October 2013.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior. For more information about this release, see the
+         * <a href="/about/versions/kitkat/">Android KitKat overview</a>.</p>
+         * <ul>
+         * <li> The default result of
+         * {@link android.preference.PreferenceActivity#isValidFragment(String)
+         * PreferenceActivity.isValueFragment} becomes false instead of true.</li>
+         * <li> In {@link android.webkit.WebView}, apps targeting earlier versions will have
+         * JS URLs evaluated directly and any result of the evaluation will not replace
+         * the current page content.  Apps targetting KITKAT or later that load a JS URL will
+         * have the result of that URL replace the content of the current page</li>
+         * <li> {@link android.app.AlarmManager#set AlarmManager.set} becomes interpreted as
+         * an inexact value, to give the system more flexibility in scheduling alarms.</li>
+         * <li> {@link android.content.Context#getSharedPreferences(String, int)
+         * Context.getSharedPreferences} no longer allows a null name.</li>
+         * <li> {@link android.widget.RelativeLayout} changes to compute wrapped content
+         * margins correctly.</li>
+         * <li> {@link android.app.ActionBar}'s window content overlay is allowed to be
+         * drawn.</li>
+         * <li>The {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}
+         * permission is now always enforced.</li>
+         * <li>Access to package-specific external storage directories belonging
+         * to the calling app no longer requires the
+         * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} or
+         * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
+         * permissions.</li>
+         * </ul>
+         */
+        public static final int KITKAT = 19;
+
+        /**
+         * K for watches.
+         *
+         * <p>Released publicly as Android 4.4W in June 2014.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li>{@link android.app.AlertDialog} might not have a default background if the theme does
+         * not specify one.</li>
+         * </ul>
+         */
+        public static final int KITKAT_WATCH = 20;
+
+        /**
+         * Temporary until we completely switch to {@link #LOLLIPOP}.
+         * @hide
+         */
+        public static final int L = 21;
+
+        /**
+         * L.
+         *
+         * <p>Released publicly as Android 5.0 in November 2014.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior.  For more information about this release, see the
+         * <a href="/about/versions/lollipop/">Android Lollipop overview</a>.</p>
+         * <ul>
+         * <li> {@link android.content.Context#bindService Context.bindService} now
+         * requires an explicit Intent, and will throw an exception if given an implicit
+         * Intent.</li>
+         * <li> {@link android.app.Notification.Builder Notification.Builder} will
+         * not have the colors of their various notification elements adjusted to better
+         * match the new material design look.</li>
+         * <li> {@link android.os.Message} will validate that a message is not currently
+         * in use when it is recycled.</li>
+         * <li> Hardware accelerated drawing in windows will be enabled automatically
+         * in most places.</li>
+         * <li> {@link android.widget.Spinner} throws an exception if attaching an
+         * adapter with more than one item type.</li>
+         * <li> If the app is a launcher, the launcher will be available to the user
+         * even when they are using corporate profiles (which requires that the app
+         * use {@link android.content.pm.LauncherApps} to correctly populate its
+         * apps UI).</li>
+         * <li> Calling {@link android.app.Service#stopForeground Service.stopForeground}
+         * with removeNotification false will modify the still posted notification so that
+         * it is no longer forced to be ongoing.</li>
+         * <li> A {@link android.service.dreams.DreamService} must require the
+         * {@link android.Manifest.permission#BIND_DREAM_SERVICE} permission to be usable.</li>
+         * </ul>
+         */
+        public static final int LOLLIPOP = 21;
+
+        /**
+         * L MR1.
+         *
+         * <p>Released publicly as Android 5.1 in March 2015.
+         * <p>For more information about this release, see the
+         * <a href="/about/versions/android-5.1">Android 5.1 APIs</a>.
+         */
+        public static final int LOLLIPOP_MR1 = 22;
+
+        /**
+         * M.
+         *
+         * <p>Released publicly as Android 6.0 in October 2015.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior. For more information about this release, see the
+         * <a href="/about/versions/marshmallow/">Android 6.0 Marshmallow overview</a>.</p>
+         * <ul>
+         * <li> Runtime permissions.  Dangerous permissions are no longer granted at
+         * install time, but must be requested by the application at runtime through
+         * {@link android.app.Activity#requestPermissions}.</li>
+         * <li> Bluetooth and Wi-Fi scanning now requires holding the location permission.</li>
+         * <li> {@link android.app.AlarmManager#setTimeZone AlarmManager.setTimeZone} will fail if
+         * the given timezone is non-Olson.</li>
+         * <li> Activity transitions will only return shared
+         * elements mapped in the returned view hierarchy back to the calling activity.</li>
+         * <li> {@link android.view.View} allows a number of behaviors that may break
+         * existing apps: Canvas throws an exception if restore() is called too many times,
+         * widgets may return a hint size when returning UNSPECIFIED measure specs, and it
+         * will respect the attributes {@link android.R.attr#foreground},
+         * {@link android.R.attr#foregroundGravity}, {@link android.R.attr#foregroundTint}, and
+         * {@link android.R.attr#foregroundTintMode}.</li>
+         * <li> {@link android.view.MotionEvent#getButtonState MotionEvent.getButtonState}
+         * will no longer report {@link android.view.MotionEvent#BUTTON_PRIMARY}
+         * and {@link android.view.MotionEvent#BUTTON_SECONDARY} as synonyms for
+         * {@link android.view.MotionEvent#BUTTON_STYLUS_PRIMARY} and
+         * {@link android.view.MotionEvent#BUTTON_STYLUS_SECONDARY}.</li>
+         * <li> {@link android.widget.ScrollView} now respects the layout param margins
+         * when measuring.</li>
+         * </ul>
+         */
+        public static final int M = 23;
+
+        /**
+         * N.
+         *
+         * <p>Released publicly as Android 7.0 in August 2016.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior. For more information about this release, see
+         * the <a href="/about/versions/nougat/">Android Nougat overview</a>.</p>
+         * <ul>
+         * <li> {@link android.app.DownloadManager.Request#setAllowedNetworkTypes
+         * DownloadManager.Request.setAllowedNetworkTypes}
+         * will disable "allow over metered" when specifying only
+         * {@link android.app.DownloadManager.Request#NETWORK_WIFI}.</li>
+         * <li> {@link android.app.DownloadManager} no longer allows access to raw
+         * file paths.</li>
+         * <li> {@link android.app.Notification.Builder#setShowWhen
+         * Notification.Builder.setShowWhen}
+         * must be called explicitly to have the time shown, and various other changes in
+         * {@link android.app.Notification.Builder Notification.Builder} to how notifications
+         * are shown.</li>
+         * <li>{@link android.content.Context#MODE_WORLD_READABLE} and
+         * {@link android.content.Context#MODE_WORLD_WRITEABLE} are no longer supported.</li>
+         * <li>{@link android.os.FileUriExposedException} will be thrown to applications.</li>
+         * <li>Applications will see global drag and drops as per
+         * {@link android.view.View#DRAG_FLAG_GLOBAL}.</li>
+         * <li>{@link android.webkit.WebView#evaluateJavascript WebView.evaluateJavascript}
+         * will not persist state from an empty WebView.</li>
+         * <li>{@link android.animation.AnimatorSet} will not ignore calls to end() before
+         * start().</li>
+         * <li>{@link android.app.AlarmManager#cancel(android.app.PendingIntent)
+         * AlarmManager.cancel} will throw a NullPointerException if given a null operation.</li>
+         * <li>{@link android.app.FragmentManager} will ensure fragments have been created
+         * before being placed on the back stack.</li>
+         * <li>{@link android.app.FragmentManager} restores fragments in
+         * {@link android.app.Fragment#onCreate Fragment.onCreate} rather than after the
+         * method returns.</li>
+         * <li>{@link android.R.attr#resizeableActivity} defaults to true.</li>
+         * <li>{@link android.graphics.drawable.AnimatedVectorDrawable} throws exceptions when
+         * opening invalid VectorDrawable animations.</li>
+         * <li>{@link android.view.ViewGroup.MarginLayoutParams} will no longer be dropped
+         * when converting between some types of layout params (such as
+         * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} to
+         * {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams}).</li>
+         * <li>Your application processes will not be killed when the device density changes.</li>
+         * <li>Drag and drop. After a view receives the
+         * {@link android.view.DragEvent#ACTION_DRAG_ENTERED} event, when the drag shadow moves into
+         * a descendant view that can accept the data, the view receives the
+         * {@link android.view.DragEvent#ACTION_DRAG_EXITED} event and won’t receive
+         * {@link android.view.DragEvent#ACTION_DRAG_LOCATION} and
+         * {@link android.view.DragEvent#ACTION_DROP} events while the drag shadow is within that
+         * descendant view, even if the descendant view returns <code>false</code> from its handler
+         * for these events.</li>
+         * </ul>
+         */
+        public static final int N = 24;
+
+        /**
+         * N MR1.
+         *
+         * <p>Released publicly as Android 7.1 in October 2016.
+         * <p>For more information about this release, see
+         * <a href="/about/versions/nougat/android-7.1">Android 7.1 for
+         * Developers</a>.
+         */
+        public static final int N_MR1 = 25;
+
+        /**
+         * O.
+         *
+         * <p>Released publicly as Android 8.0 in August 2017.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior. For more information about this release, see
+         * the <a href="/about/versions/oreo/">Android Oreo overview</a>.</p>
+         * <ul>
+         * <li><a href="{@docRoot}about/versions/oreo/background.html">Background execution limits</a>
+         * are applied to the application.</li>
+         * <li>The behavior of AccountManager's
+         * {@link android.accounts.AccountManager#getAccountsByType},
+         * {@link android.accounts.AccountManager#getAccountsByTypeAndFeatures}, and
+         * {@link android.accounts.AccountManager#hasFeatures} has changed as documented there.</li>
+         * <li>{@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE_PRE_26}
+         * is now returned as
+         * {@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE}.</li>
+         * <li>The {@link android.app.NotificationManager} now requires the use of notification
+         * channels.</li>
+         * <li>Changes to the strict mode that are set in
+         * {@link Application#onCreate Application.onCreate} will no longer be clobbered after
+         * that function returns.</li>
+         * <li>A shared library apk with native code will have that native code included in
+         * the library path of its clients.</li>
+         * <li>{@link android.content.Context#getSharedPreferences Context.getSharedPreferences}
+         * in credential encrypted storage will throw an exception before the user is unlocked.</li>
+         * <li>Attempting to retrieve a {@link Context#FINGERPRINT_SERVICE} on a device that
+         * does not support that feature will now throw a runtime exception.</li>
+         * <li>{@link android.app.Fragment} will stop any active view animations when
+         * the fragment is stopped.</li>
+         * <li>Some compatibility code in Resources that attempts to use the default Theme
+         * the app may be using will be turned off, requiring the app to explicitly request
+         * resources with the right theme.</li>
+         * <li>{@link android.content.ContentResolver#notifyChange ContentResolver.notifyChange} and
+         * {@link android.content.ContentResolver#registerContentObserver
+         * ContentResolver.registerContentObserver}
+         * will throw a SecurityException if the caller does not have permission to access
+         * the provider (or the provider doesn't exit); otherwise the call will be silently
+         * ignored.</li>
+         * <li>{@link android.hardware.camera2.CameraDevice#createCaptureRequest
+         * CameraDevice.createCaptureRequest} will enable
+         * {@link android.hardware.camera2.CaptureRequest#CONTROL_ENABLE_ZSL} by default for
+         * still image capture.</li>
+         * <li>WallpaperManager's {@link android.app.WallpaperManager#getWallpaperFile},
+         * {@link android.app.WallpaperManager#getDrawable},
+         * {@link android.app.WallpaperManager#getFastDrawable},
+         * {@link android.app.WallpaperManager#peekDrawable}, and
+         * {@link android.app.WallpaperManager#peekFastDrawable} will throw an exception
+         * if you can not access the wallpaper.</li>
+         * <li>The behavior of
+         * {@link android.hardware.usb.UsbDeviceConnection#requestWait UsbDeviceConnection.requestWait}
+         * is modified as per the documentation there.</li>
+         * <li>{@link StrictMode.VmPolicy.Builder#detectAll StrictMode.VmPolicy.Builder.detectAll}
+         * will also enable {@link StrictMode.VmPolicy.Builder#detectContentUriWithoutPermission}
+         * and {@link StrictMode.VmPolicy.Builder#detectUntaggedSockets}.</li>
+         * <li>{@link StrictMode.ThreadPolicy.Builder#detectAll StrictMode.ThreadPolicy.Builder.detectAll}
+         * will also enable {@link StrictMode.ThreadPolicy.Builder#detectUnbufferedIo}.</li>
+         * <li>{@link android.provider.DocumentsContract}'s various methods will throw failure
+         * exceptions back to the caller instead of returning null.
+         * <li>{@link View#hasFocusable() View.hasFocusable} now includes auto-focusable views.</li>
+         * <li>{@link android.view.SurfaceView} will no longer always change the underlying
+         * Surface object when something about it changes; apps need to look at the current
+         * state of the object to determine which things they are interested in have changed.</li>
+         * <li>{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} must be
+         * used for overlay windows, other system overlay window types are not allowed.</li>
+         * <li>{@link android.view.ViewTreeObserver#addOnDrawListener
+         * ViewTreeObserver.addOnDrawListener} will throw an exception if called from within
+         * onDraw.</li>
+         * <li>{@link android.graphics.Canvas#setBitmap Canvas.setBitmap} will no longer preserve
+         * the current matrix and clip stack of the canvas.</li>
+         * <li>{@link android.widget.ListPopupWindow#setHeight ListPopupWindow.setHeight}
+         * will throw an exception if a negative height is supplied.</li>
+         * <li>{@link android.widget.TextView} will use internationalized input for numbers,
+         * dates, and times.</li>
+         * <li>{@link android.widget.Toast} must be used for showing toast windows; the toast
+         * window type can not be directly used.</li>
+         * <li>{@link android.net.wifi.WifiManager#getConnectionInfo WifiManager.getConnectionInfo}
+         * requires that the caller hold the location permission to return BSSID/SSID</li>
+         * <li>{@link android.net.wifi.p2p.WifiP2pManager#requestPeers WifiP2pManager.requestPeers}
+         * requires the caller hold the location permission.</li>
+         * <li>{@link android.R.attr#maxAspectRatio} defaults to 0, meaning there is no restriction
+         * on the app's maximum aspect ratio (so it can be stretched to fill larger screens).</li>
+         * <li>{@link android.R.attr#focusable} defaults to a new state ({@code auto}) where it will
+         * inherit the value of {@link android.R.attr#clickable} unless explicitly overridden.</li>
+         * <li>A default theme-appropriate focus-state highlight will be supplied to all Views
+         * which don't provide a focus-state drawable themselves. This can be disabled by setting
+         * {@link android.R.attr#defaultFocusHighlightEnabled} to false.</li>
+         * </ul>
+         */
+        public static final int O = 26;
+
+        /**
+         * O MR1.
+         *
+         * <p>Released publicly as Android 8.1 in December 2017.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior. For more information about this release, see
+         * <a href="/about/versions/oreo/android-8.1">Android 8.1 features and
+         * APIs</a>.</p>
+         * <ul>
+         * <li>Apps exporting and linking to apk shared libraries must explicitly
+         * enumerate all signing certificates in a consistent order.</li>
+         * <li>{@link android.R.attr#screenOrientation} can not be used to request a fixed
+         * orientation if the associated activity is not fullscreen and opaque.</li>
+         * </ul>
+         *
+         */
+        public static final int O_MR1 = 27;
+
+        /**
+         * P.
+         *
+         * <p>Released publicly as Android 9 in August 2018.
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior. For more information about this release, see the
+         * <a href="/about/versions/pie/">Android 9 Pie overview</a>.</p>
+         * <ul>
+         * <li>{@link android.app.Service#startForeground Service.startForeground} requires
+         * that apps hold the permission
+         * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li>
+         * <li>{@link android.widget.LinearLayout} will always remeasure weighted children,
+         * even if there is no excess space.</li>
+         * </ul>
+         *
+         */
+        public static final int P = 28;
+
+        /**
+         * Q.
+         *
+         * <p>Released publicly as Android 10 in September 2019.
+         * <p>Applications targeting this or a later release will get these new changes in behavior.
+         * For more information about this release, see the
+         * <a href="/about/versions/10">Android 10 overview</a>.</p>
+         * <ul>
+         * <li><a href="/about/versions/10/behavior-changes-all">Behavior changes: all apps</a></li>
+         * <li><a href="/about/versions/10/behavior-changes-10">Behavior changes: apps targeting API
+         * 29+</a></li>
+         * </ul>
+         *
+         */
+        public static final int Q = 29;
+
+        /**
+         * R.
+         *
+         * <p>Released publicly as Android 11 in September 2020.
+         * <p>Applications targeting this or a later release will get these new changes in behavior.
+         * For more information about this release, see the
+         * <a href="/about/versions/11">Android 11 overview</a>.</p>
+         * <ul>
+         * <li><a href="/about/versions/11/behavior-changes-all">Behavior changes: all apps</a></li>
+         * <li><a href="/about/versions/11/behavior-changes-11">Behavior changes: Apps targeting
+         * Android 11</a></li>
+         * <li><a href="/about/versions/11/non-sdk-11">Updates to non-SDK interface restrictions
+         * in Android 11</a></li>
+         * </ul>
+         *
+         */
+        public static final int R = 30;
+
+        /**
+         * S.
+         */
+        public static final int S = 31;
+
+        /**
+         * S V2.
+         *
+         * Once more unto the breach, dear friends, once more.
+         */
+        public static final int S_V2 = 32;
+
+        /**
+         * Tiramisu.
+         */
+        public static final int TIRAMISU = 33;
+
+        /**
+         * Upside Down Cake.
+         */
+        public static final int UPSIDE_DOWN_CAKE = 34;
+    }
+
+    /** The type of build, like "user" or "eng". */
+    public static final String TYPE = getString("ro.build.type");
+
+    /** Comma-separated tags describing the build, like "unsigned,debug". */
+    public static final String TAGS = getString("ro.build.tags");
+
+    /** A string that uniquely identifies this build.  Do not attempt to parse this value. */
+    public static final String FINGERPRINT = deriveFingerprint();
+
+    /**
+     * Some devices split the fingerprint components between multiple
+     * partitions, so we might derive the fingerprint at runtime.
+     */
+    private static String deriveFingerprint() {
+        String finger = SystemProperties.get("ro.build.fingerprint");
+        if (TextUtils.isEmpty(finger)) {
+            finger = getString("ro.product.brand") + '/' +
+                    getString("ro.product.name") + '/' +
+                    getString("ro.product.device") + ':' +
+                    getString("ro.build.version.release") + '/' +
+                    getString("ro.build.id") + '/' +
+                    getString("ro.build.version.incremental") + ':' +
+                    getString("ro.build.type") + '/' +
+                    getString("ro.build.tags");
+        }
+        return finger;
+    }
+
+    /**
+     * Ensure that raw fingerprint system property is defined. If it was derived
+     * dynamically by {@link #deriveFingerprint()} this is where we push the
+     * derived value into the property service.
+     *
+     * @hide
+     */
+    public static void ensureFingerprintProperty() {
+        if (TextUtils.isEmpty(SystemProperties.get("ro.build.fingerprint"))) {
+            try {
+                SystemProperties.set("ro.build.fingerprint", FINGERPRINT);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Failed to set fingerprint property", e);
+            }
+        }
+    }
+
+    /**
+     * A multiplier for various timeouts on the system.
+     *
+     * The intent is that products targeting software emulators that are orders of magnitude slower
+     * than real hardware may set this to a large number. On real devices and hardware-accelerated
+     * virtualized devices this should not be set.
+     *
+     * @hide
+     */
+    public static final int HW_TIMEOUT_MULTIPLIER =
+        SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
+
+    /**
+     * True if Treble is enabled and required for this device.
+     *
+     * @hide
+     */
+    public static final boolean IS_TREBLE_ENABLED =
+        SystemProperties.getBoolean("ro.treble.enabled", false);
+
+    /**
+     * Verifies the current flash of the device is consistent with what
+     * was expected at build time.
+     *
+     * Treble devices will verify the Vendor Interface (VINTF). A device
+     * launched without Treble:
+     *
+     * 1) Checks that device fingerprint is defined and that it matches across
+     *    various partitions.
+     * 2) Verifies radio and bootloader partitions are those expected in the build.
+     *
+     * @hide
+     */
+    public static boolean isBuildConsistent() {
+        // Don't care on eng builds.  Incremental build may trigger false negative.
+        if (IS_ENG) return true;
+
+        if (IS_TREBLE_ENABLED) {
+            // If we can run this code, the device should already pass AVB.
+            // So, we don't need to check AVB here.
+            int result = VintfObject.verifyWithoutAvb();
+
+            if (result != 0) {
+                Slog.e(TAG, "Vendor interface is incompatible, error="
+                        + String.valueOf(result));
+            }
+
+            return result == 0;
+        }
+
+        final String system = SystemProperties.get("ro.system.build.fingerprint");
+        final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
+        final String bootimage = SystemProperties.get("ro.bootimage.build.fingerprint");
+        final String requiredBootloader = SystemProperties.get("ro.build.expect.bootloader");
+        final String currentBootloader = SystemProperties.get("ro.bootloader");
+        final String requiredRadio = SystemProperties.get("ro.build.expect.baseband");
+        final String currentRadio = joinListOrElse(
+                TelephonyProperties.baseband_version(), "");
+
+        if (TextUtils.isEmpty(system)) {
+            Slog.e(TAG, "Required ro.system.build.fingerprint is empty!");
+            return false;
+        }
+
+        if (!TextUtils.isEmpty(vendor)) {
+            if (!Objects.equals(system, vendor)) {
+                Slog.e(TAG, "Mismatched fingerprints; system reported " + system
+                        + " but vendor reported " + vendor);
+                return false;
+            }
+        }
+
+        /* TODO: Figure out issue with checks failing
+        if (!TextUtils.isEmpty(bootimage)) {
+            if (!Objects.equals(system, bootimage)) {
+                Slog.e(TAG, "Mismatched fingerprints; system reported " + system
+                        + " but bootimage reported " + bootimage);
+                return false;
+            }
+        }
+
+        if (!TextUtils.isEmpty(requiredBootloader)) {
+            if (!Objects.equals(currentBootloader, requiredBootloader)) {
+                Slog.e(TAG, "Mismatched bootloader version: build requires " + requiredBootloader
+                        + " but runtime reports " + currentBootloader);
+                return false;
+            }
+        }
+
+        if (!TextUtils.isEmpty(requiredRadio)) {
+            if (!Objects.equals(currentRadio, requiredRadio)) {
+                Slog.e(TAG, "Mismatched radio version: build requires " + requiredRadio
+                        + " but runtime reports " + currentRadio);
+                return false;
+            }
+        }
+        */
+
+        return true;
+    }
+
+    /** Build information for a particular device partition. */
+    public static class Partition {
+        /** The name identifying the system partition. */
+        public static final String PARTITION_NAME_SYSTEM = "system";
+        /** @hide */
+        public static final String PARTITION_NAME_BOOTIMAGE = "bootimage";
+        /** @hide */
+        public static final String PARTITION_NAME_ODM = "odm";
+        /** @hide */
+        public static final String PARTITION_NAME_OEM = "oem";
+        /** @hide */
+        public static final String PARTITION_NAME_PRODUCT = "product";
+        /** @hide */
+        public static final String PARTITION_NAME_SYSTEM_EXT = "system_ext";
+        /** @hide */
+        public static final String PARTITION_NAME_VENDOR = "vendor";
+
+        private final String mName;
+        private final String mFingerprint;
+        private final long mTimeMs;
+
+        private Partition(String name, String fingerprint, long timeMs) {
+            mName = name;
+            mFingerprint = fingerprint;
+            mTimeMs = timeMs;
+        }
+
+        /** The name of this partition, e.g. "system", or "vendor" */
+        @NonNull
+        public String getName() {
+            return mName;
+        }
+
+        /** The build fingerprint of this partition, see {@link Build#FINGERPRINT}. */
+        @NonNull
+        public String getFingerprint() {
+            return mFingerprint;
+        }
+
+        /** The time (ms since epoch), at which this partition was built, see {@link Build#TIME}. */
+        public long getBuildTimeMillis() {
+            return mTimeMs;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (!(o instanceof Partition)) {
+                return false;
+            }
+            Partition op = (Partition) o;
+            return mName.equals(op.mName)
+                    && mFingerprint.equals(op.mFingerprint)
+                    && mTimeMs == op.mTimeMs;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mName, mFingerprint, mTimeMs);
+        }
+    }
+
+    /**
+     * Get build information about partitions that have a separate fingerprint defined.
+     *
+     * The list includes partitions that are suitable candidates for over-the-air updates. This is
+     * not an exhaustive list of partitions on the device.
+     */
+    @NonNull
+    public static List<Partition> getFingerprintedPartitions() {
+        ArrayList<Partition> partitions = new ArrayList();
+
+        String[] names = new String[] {
+                Partition.PARTITION_NAME_BOOTIMAGE,
+                Partition.PARTITION_NAME_ODM,
+                Partition.PARTITION_NAME_PRODUCT,
+                Partition.PARTITION_NAME_SYSTEM_EXT,
+                Partition.PARTITION_NAME_SYSTEM,
+                Partition.PARTITION_NAME_VENDOR
+        };
+        for (String name : names) {
+            String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint");
+            if (TextUtils.isEmpty(fingerprint)) {
+                continue;
+            }
+            long time = getLong("ro." + name + ".build.date.utc") * 1000;
+            partitions.add(new Partition(name, fingerprint, time));
+        }
+
+        return partitions;
+    }
+
+    // The following properties only make sense for internal engineering builds.
+
+    /** The time at which the build was produced, given in milliseconds since the UNIX epoch. */
+    public static final long TIME = getLong("ro.build.date.utc") * 1000;
+    public static final String USER = getString("ro.build.user");
+    public static final String HOST = getString("ro.build.host");
+
+    /**
+     * Returns true if the device is running a debuggable build such as "userdebug" or "eng".
+     *
+     * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
+     * application regardless of whether they have the "debuggable" attribute set, or downgrade
+     * selinux into "permissive" mode in particular.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final boolean IS_DEBUGGABLE =
+            SystemProperties.getInt("ro.debuggable", 0) == 1;
+
+    /**
+     * Returns true if the device is running a debuggable build such as "userdebug" or "eng".
+     *
+     * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
+     * application regardless of whether they have the "debuggable" attribute set, or downgrade
+     * selinux into "permissive" mode in particular.
+     * @hide
+     */
+    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static boolean isDebuggable() {
+        return IS_DEBUGGABLE;
+    }
+
+    /** {@hide} */
+    public static final boolean IS_ENG = "eng".equals(TYPE);
+    /** {@hide} */
+    public static final boolean IS_USERDEBUG = "userdebug".equals(TYPE);
+    /** {@hide} */
+    public static final boolean IS_USER = "user".equals(TYPE);
+
+    /**
+     * Whether this build is running on ARC, the Android Runtime for Chrome
+     * (https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md).
+     * Prior to R this was implemented as a container but from R this will be
+     * a VM. The name of the property remains ro.boot.conntainer as it is
+     * referenced in other projects.
+     *
+     * We should try to avoid checking this flag if possible to minimize
+     * unnecessarily diverging from non-container Android behavior.
+     * Checking this flag is acceptable when low-level resources being
+     * different, e.g. the availability of certain capabilities, access to
+     * system resources being restricted, and the fact that the host OS might
+     * handle some features for us.
+     * For higher-level behavior differences, other checks should be preferred.
+     * @hide
+     */
+    public static final boolean IS_ARC =
+            SystemProperties.getBoolean("ro.boot.container", false);
+
+    /**
+     * Specifies whether the permissions needed by a legacy app should be
+     * reviewed before any of its components can run. A legacy app is one
+     * with targetSdkVersion < 23, i.e apps using the old permission model.
+     * If review is not required, permissions are reviewed before the app
+     * is installed.
+     *
+     * @hide
+     * @removed
+     */
+    @SystemApi
+    public static final boolean PERMISSIONS_REVIEW_REQUIRED = true;
+
+    /**
+     * Returns the version string for the radio firmware.  May return
+     * null (if, for instance, the radio is not currently on).
+     */
+    public static String getRadioVersion() {
+        return joinListOrElse(TelephonyProperties.baseband_version(), null);
+    }
+
+    @UnsupportedAppUsage
+    private static String getString(String property) {
+        return SystemProperties.get(property, UNKNOWN);
+    }
+    /**
+     * Return attestation specific proerties.
+     * @param property model, name, brand, device or manufacturer.
+     * @return property value or UNKNOWN
+     */
+    private static String getVendorDeviceIdProperty(String property) {
+        String attestProp = getString(
+                TextUtils.formatSimple("ro.product.%s_for_attestation", property));
+        return attestProp.equals(UNKNOWN)
+                ? getString(TextUtils.formatSimple("ro.product.vendor.%s", property)) : UNKNOWN;
+    }
+
+    private static String[] getStringList(String property, String separator) {
+        String value = SystemProperties.get(property);
+        if (value.isEmpty()) {
+            return new String[0];
+        } else {
+            return value.split(separator);
+        }
+    }
+
+    @UnsupportedAppUsage
+    private static long getLong(String property) {
+        try {
+            return Long.parseLong(SystemProperties.get(property));
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+
+    private static <T> String joinListOrElse(List<T> list, String defaultValue) {
+        String ret = list.stream().map(elem -> elem == null ? "" : elem.toString())
+                .collect(Collectors.joining(","));
+        return ret.isEmpty() ? defaultValue : ret;
+    }
+}
diff --git a/android-34/android/os/Bundle.java b/android-34/android/os/Bundle.java
new file mode 100644
index 0000000..e845ffa
--- /dev/null
+++ b/android-34/android/os/Bundle.java
@@ -0,0 +1,1453 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.ArrayMap;
+import android.util.Size;
+import android.util.SizeF;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A mapping from String keys to various {@link Parcelable} values.
+ *
+ * <p><b>Warning:</b> Note that {@link Bundle} is a lazy container and as such it does NOT implement
+ * {@link #equals(Object)} or {@link #hashCode()}.
+ *
+ * @see PersistableBundle
+ */
+public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
+    @VisibleForTesting
+    static final int FLAG_HAS_FDS = 1 << 8;
+
+    @VisibleForTesting
+    static final int FLAG_HAS_FDS_KNOWN = 1 << 9;
+
+    @VisibleForTesting
+    static final int FLAG_ALLOW_FDS = 1 << 10;
+
+    /** An unmodifiable {@code Bundle} that is always {@link #isEmpty() empty}. */
+    public static final Bundle EMPTY;
+
+    /**
+     * Special extras used to denote extras have been stripped off.
+     * @hide
+     */
+    public static final Bundle STRIPPED;
+
+    static {
+        EMPTY = new Bundle();
+        EMPTY.mMap = ArrayMap.EMPTY;
+
+        STRIPPED = new Bundle();
+        STRIPPED.putInt("STRIPPED", 1);
+    }
+
+    /**
+     * Constructs a new, empty Bundle.
+     */
+    public Bundle() {
+        super();
+        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
+    }
+
+    /**
+     * Constructs a Bundle whose data is stored as a Parcel.  The data
+     * will be unparcelled on first contact, using the assigned ClassLoader.
+     *
+     * @param parcelledData a Parcel containing a Bundle
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public Bundle(Parcel parcelledData) {
+        super(parcelledData);
+        mFlags = FLAG_ALLOW_FDS;
+        maybePrefillHasFds();
+    }
+
+    /**
+     * Constructor from a parcel for when the length is known *and is not stored in the parcel.*
+     * The other constructor that takes a parcel assumes the length is in the parcel.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public Bundle(Parcel parcelledData, int length) {
+        super(parcelledData, length);
+        mFlags = FLAG_ALLOW_FDS;
+        maybePrefillHasFds();
+    }
+
+    /**
+     * Constructs a {@link Bundle} containing a copy of {@code from}.
+     *
+     * @param from The bundle to be copied.
+     * @param deep Whether is a deep or shallow copy.
+     *
+     * @hide
+     */
+    Bundle(Bundle from, boolean deep) {
+        super(from, deep);
+    }
+
+    /**
+     * If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast.
+     * Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN}
+     * unset, because scanning a map is slower.  We'll do it lazily in
+     * {@link #hasFileDescriptors()}.
+     */
+    private void maybePrefillHasFds() {
+        if (mParcelledData != null) {
+            if (mParcelledData.hasFileDescriptors()) {
+                mFlags |= FLAG_HAS_FDS | FLAG_HAS_FDS_KNOWN;
+            } else {
+                mFlags |= FLAG_HAS_FDS_KNOWN;
+            }
+        }
+    }
+
+    /**
+     * Constructs a new, empty Bundle that uses a specific ClassLoader for
+     * instantiating Parcelable and Serializable objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     */
+    public Bundle(ClassLoader loader) {
+        super(loader);
+        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
+    }
+
+    /**
+     * Constructs a new, empty Bundle sized to hold the given number of
+     * elements. The Bundle will grow as needed.
+     *
+     * @param capacity the initial capacity of the Bundle
+     */
+    public Bundle(int capacity) {
+        super(capacity);
+        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
+    }
+
+    /**
+     * Constructs a Bundle containing a copy of the mappings from the given
+     * Bundle.  Does only a shallow copy of the original Bundle -- see
+     * {@link #deepCopy()} if that is not what you want.
+     *
+     * @param b a Bundle to be copied.
+     *
+     * @see #deepCopy()
+     */
+    public Bundle(Bundle b) {
+        super(b);
+        mFlags = b.mFlags;
+    }
+
+    /**
+     * Constructs a Bundle containing a copy of the mappings from the given
+     * PersistableBundle.  Does only a shallow copy of the PersistableBundle -- see
+     * {@link PersistableBundle#deepCopy()} if you don't want that.
+     *
+     * @param b a PersistableBundle to be copied.
+     */
+    public Bundle(PersistableBundle b) {
+        super(b);
+        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
+    }
+
+    /**
+     * Make a Bundle for a single key/value pair.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static Bundle forPair(String key, String value) {
+        Bundle b = new Bundle(1);
+        b.putString(key, value);
+        return b;
+    }
+
+    /**
+     * Changes the ClassLoader this Bundle uses when instantiating objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     */
+    @Override
+    public void setClassLoader(ClassLoader loader) {
+        super.setClassLoader(loader);
+    }
+
+    /**
+     * Return the ClassLoader currently associated with this Bundle.
+     */
+    @Override
+    public ClassLoader getClassLoader() {
+        return super.getClassLoader();
+    }
+
+    /** {@hide} */
+    public boolean setAllowFds(boolean allowFds) {
+        final boolean orig = (mFlags & FLAG_ALLOW_FDS) != 0;
+        if (allowFds) {
+            mFlags |= FLAG_ALLOW_FDS;
+        } else {
+            mFlags &= ~FLAG_ALLOW_FDS;
+        }
+        return orig;
+    }
+
+    /**
+     * Mark if this Bundle is okay to "defuse." That is, it's okay for system
+     * processes to ignore any {@link BadParcelableException} encountered when
+     * unparceling it, leaving an empty bundle in its place.
+     * <p>
+     * This should <em>only</em> be set when the Bundle reaches its final
+     * destination, otherwise a system process may clobber contents that were
+     * destined for an app that could have unparceled them.
+     *
+     * @hide
+     */
+    public void setDefusable(boolean defusable) {
+        if (defusable) {
+            mFlags |= FLAG_DEFUSABLE;
+        } else {
+            mFlags &= ~FLAG_DEFUSABLE;
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public static Bundle setDefusable(Bundle bundle, boolean defusable) {
+        if (bundle != null) {
+            bundle.setDefusable(defusable);
+        }
+        return bundle;
+    }
+
+    /**
+     * Clones the current Bundle. The internal map is cloned, but the keys and
+     * values to which it refers are copied by reference.
+     */
+    @Override
+    public Object clone() {
+        return new Bundle(this);
+    }
+
+    /**
+     * Make a deep copy of the given bundle.  Traverses into inner containers and copies
+     * them as well, so they are not shared across bundles.  Will traverse in to
+     * {@link Bundle}, {@link PersistableBundle}, {@link ArrayList}, and all types of
+     * primitive arrays.  Other types of objects (such as Parcelable or Serializable)
+     * are referenced as-is and not copied in any way.
+     */
+    public Bundle deepCopy() {
+        return new Bundle(this, /* deep */ true);
+    }
+
+    /**
+     * Removes all elements from the mapping of this Bundle.
+     */
+    @Override
+    public void clear() {
+        super.clear();
+        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
+    }
+
+    /**
+     * Removes any entry with the given key from the mapping of this Bundle.
+     *
+     * @param key a String key
+     */
+    public void remove(String key) {
+        super.remove(key);
+        if ((mFlags & FLAG_HAS_FDS) != 0) {
+            mFlags &= ~FLAG_HAS_FDS_KNOWN;
+        }
+    }
+
+    /**
+     * Inserts all mappings from the given Bundle into this Bundle.
+     *
+     * @param bundle a Bundle
+     */
+    public void putAll(Bundle bundle) {
+        unparcel();
+        bundle.unparcel();
+        mOwnsLazyValues = false;
+        bundle.mOwnsLazyValues = false;
+        mMap.putAll(bundle.mMap);
+
+        // FD state is now known if and only if both bundles already knew
+        if ((bundle.mFlags & FLAG_HAS_FDS) != 0) {
+            mFlags |= FLAG_HAS_FDS;
+        }
+        if ((bundle.mFlags & FLAG_HAS_FDS_KNOWN) == 0) {
+            mFlags &= ~FLAG_HAS_FDS_KNOWN;
+        }
+    }
+
+    /**
+     * Return the size of {@link #mParcelledData} in bytes if available, otherwise {@code 0}.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int getSize() {
+        if (mParcelledData != null) {
+            return mParcelledData.dataSize();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Reports whether the bundle contains any parcelled file descriptors.
+     */
+    public boolean hasFileDescriptors() {
+        if ((mFlags & FLAG_HAS_FDS_KNOWN) == 0) {
+            Parcel p = mParcelledData;
+            mFlags = (Parcel.hasFileDescriptors((p != null) ? p : mMap))
+                    ? mFlags | FLAG_HAS_FDS
+                    : mFlags & ~FLAG_HAS_FDS;
+            mFlags |= FLAG_HAS_FDS_KNOWN;
+        }
+        return (mFlags & FLAG_HAS_FDS) != 0;
+    }
+
+    /** {@hide} */
+    @Override
+    public void putObject(@Nullable String key, @Nullable Object value) {
+        if (value instanceof Byte) {
+            putByte(key, (Byte) value);
+        } else if (value instanceof Character) {
+            putChar(key, (Character) value);
+        } else if (value instanceof Short) {
+            putShort(key, (Short) value);
+        } else if (value instanceof Float) {
+            putFloat(key, (Float) value);
+        } else if (value instanceof CharSequence) {
+            putCharSequence(key, (CharSequence) value);
+        } else if (value instanceof Parcelable) {
+            putParcelable(key, (Parcelable) value);
+        } else if (value instanceof Size) {
+            putSize(key, (Size) value);
+        } else if (value instanceof SizeF) {
+            putSizeF(key, (SizeF) value);
+        } else if (value instanceof Parcelable[]) {
+            putParcelableArray(key, (Parcelable[]) value);
+        } else if (value instanceof ArrayList) {
+            putParcelableArrayList(key, (ArrayList) value);
+        } else if (value instanceof List) {
+            putParcelableList(key, (List) value);
+        } else if (value instanceof SparseArray) {
+            putSparseParcelableArray(key, (SparseArray) value);
+        } else if (value instanceof Serializable) {
+            putSerializable(key, (Serializable) value);
+        } else if (value instanceof byte[]) {
+            putByteArray(key, (byte[]) value);
+        } else if (value instanceof short[]) {
+            putShortArray(key, (short[]) value);
+        } else if (value instanceof char[]) {
+            putCharArray(key, (char[]) value);
+        } else if (value instanceof float[]) {
+            putFloatArray(key, (float[]) value);
+        } else if (value instanceof CharSequence[]) {
+            putCharSequenceArray(key, (CharSequence[]) value);
+        } else if (value instanceof Bundle) {
+            putBundle(key, (Bundle) value);
+        } else if (value instanceof Binder) {
+            putBinder(key, (Binder) value);
+        } else if (value instanceof IBinder) {
+            putIBinder(key, (IBinder) value);
+        } else {
+            super.putObject(key, value);
+        }
+    }
+
+    /**
+     * Inserts a byte value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a byte
+     */
+    @Override
+    public void putByte(@Nullable String key, byte value) {
+        super.putByte(key, value);
+    }
+
+    /**
+     * Inserts a char value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a char
+     */
+    @Override
+    public void putChar(@Nullable String key, char value) {
+        super.putChar(key, value);
+    }
+
+    /**
+     * Inserts a short value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a short
+     */
+    @Override
+    public void putShort(@Nullable String key, short value) {
+        super.putShort(key, value);
+    }
+
+    /**
+     * Inserts a float value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a float
+     */
+    @Override
+    public void putFloat(@Nullable String key, float value) {
+        super.putFloat(key, value);
+    }
+
+    /**
+     * Inserts a CharSequence value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a CharSequence, or null
+     */
+    @Override
+    public void putCharSequence(@Nullable String key, @Nullable CharSequence value) {
+        super.putCharSequence(key, value);
+    }
+
+    /**
+     * Inserts a Parcelable value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Parcelable object, or null
+     */
+    public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
+        unparcel();
+        mMap.put(key, value);
+        mFlags &= ~FLAG_HAS_FDS_KNOWN;
+    }
+
+    /**
+     * Inserts a Size value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Size object, or null
+     */
+    public void putSize(@Nullable String key, @Nullable Size value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a SizeF value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a SizeF object, or null
+     */
+    public void putSizeF(@Nullable String key, @Nullable SizeF value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an array of Parcelable values into the mapping of this Bundle,
+     * replacing any existing value for the given key.  Either key or value may
+     * be null.
+     *
+     * @param key a String, or null
+     * @param value an array of Parcelable objects, or null
+     */
+    public void putParcelableArray(@Nullable String key, @Nullable Parcelable[] value) {
+        unparcel();
+        mMap.put(key, value);
+        mFlags &= ~FLAG_HAS_FDS_KNOWN;
+    }
+
+    /**
+     * Inserts a List of Parcelable values into the mapping of this Bundle,
+     * replacing any existing value for the given key.  Either key or value may
+     * be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList of Parcelable objects, or null
+     */
+    public void putParcelableArrayList(@Nullable String key,
+            @Nullable ArrayList<? extends Parcelable> value) {
+        unparcel();
+        mMap.put(key, value);
+        mFlags &= ~FLAG_HAS_FDS_KNOWN;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public void putParcelableList(String key, List<? extends Parcelable> value) {
+        unparcel();
+        mMap.put(key, value);
+        mFlags &= ~FLAG_HAS_FDS_KNOWN;
+    }
+
+    /**
+     * Inserts a SparceArray of Parcelable values into the mapping of this
+     * Bundle, replacing any existing value for the given key.  Either key
+     * or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a SparseArray of Parcelable objects, or null
+     */
+    public void putSparseParcelableArray(@Nullable String key,
+            @Nullable SparseArray<? extends Parcelable> value) {
+        unparcel();
+        mMap.put(key, value);
+        mFlags &= ~FLAG_HAS_FDS_KNOWN;
+    }
+
+    /**
+     * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<Integer> object, or null
+     */
+    @Override
+    public void putIntegerArrayList(@Nullable String key, @Nullable ArrayList<Integer> value) {
+        super.putIntegerArrayList(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<String> object, or null
+     */
+    @Override
+    public void putStringArrayList(@Nullable String key, @Nullable ArrayList<String> value) {
+        super.putStringArrayList(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<CharSequence> object, or null
+     */
+    @Override
+    public void putCharSequenceArrayList(@Nullable String key,
+            @Nullable ArrayList<CharSequence> value) {
+        super.putCharSequenceArrayList(key, value);
+    }
+
+    /**
+     * Inserts a Serializable value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Serializable object, or null
+     */
+    @Override
+    public void putSerializable(@Nullable String key, @Nullable Serializable value) {
+        super.putSerializable(key, value);
+    }
+
+    /**
+     * Inserts a byte array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a byte array object, or null
+     */
+    @Override
+    public void putByteArray(@Nullable String key, @Nullable byte[] value) {
+        super.putByteArray(key, value);
+    }
+
+    /**
+     * Inserts a short array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a short array object, or null
+     */
+    @Override
+    public void putShortArray(@Nullable String key, @Nullable short[] value) {
+        super.putShortArray(key, value);
+    }
+
+    /**
+     * Inserts a char array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a char array object, or null
+     */
+    @Override
+    public void putCharArray(@Nullable String key, @Nullable char[] value) {
+        super.putCharArray(key, value);
+    }
+
+    /**
+     * Inserts a float array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a float array object, or null
+     */
+    @Override
+    public void putFloatArray(@Nullable String key, @Nullable float[] value) {
+        super.putFloatArray(key, value);
+    }
+
+    /**
+     * Inserts a CharSequence array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a CharSequence array object, or null
+     */
+    @Override
+    public void putCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {
+        super.putCharSequenceArray(key, value);
+    }
+
+    /**
+     * Inserts a Bundle value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Bundle object, or null
+     */
+    public void putBundle(@Nullable String key, @Nullable Bundle value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an {@link IBinder} value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * <p class="note">You should be very careful when using this function.  In many
+     * places where Bundles are used (such as inside of Intent objects), the Bundle
+     * can live longer inside of another process than the process that had originally
+     * created it.  In that case, the IBinder you supply here will become invalid
+     * when your process goes away, and no longer usable, even if a new process is
+     * created for you later on.</p>
+     *
+     * @param key a String, or null
+     * @param value an IBinder object, or null
+     */
+    public void putBinder(@Nullable String key, @Nullable IBinder value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an IBinder value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an IBinder object, or null
+     *
+     * @deprecated
+     * @hide This is the old name of the function.
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public void putIBinder(@Nullable String key, @Nullable IBinder value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Returns the value associated with the given key, or (byte) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a byte value
+     */
+    @Override
+    public byte getByte(String key) {
+        return super.getByte(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a byte value
+     */
+    @Override
+    public Byte getByte(String key, byte defaultValue) {
+        return super.getByte(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or (char) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a char value
+     */
+    @Override
+    public char getChar(String key) {
+        return super.getChar(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a char value
+     */
+    @Override
+    public char getChar(String key, char defaultValue) {
+        return super.getChar(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or (short) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a short value
+     */
+    @Override
+    public short getShort(String key) {
+        return super.getShort(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a short value
+     */
+    @Override
+    public short getShort(String key, short defaultValue) {
+        return super.getShort(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0.0f if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a float value
+     */
+    @Override
+    public float getFloat(String key) {
+        return super.getFloat(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a float value
+     */
+    @Override
+    public float getFloat(String key, float defaultValue) {
+        return super.getFloat(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a CharSequence value, or null
+     */
+    @Override
+    @Nullable
+    public CharSequence getCharSequence(@Nullable String key) {
+        return super.getCharSequence(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key or if a null
+     * value is explicitly associatd with the given key.
+     *
+     * @param key a String, or null
+     * @param defaultValue Value to return if key does not exist or if a null
+     *     value is associated with the given key.
+     * @return the CharSequence value associated with the given key, or defaultValue
+     *     if no valid CharSequence object is currently mapped to that key.
+     */
+    @Override
+    public CharSequence getCharSequence(@Nullable String key, CharSequence defaultValue) {
+        return super.getCharSequence(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Size value, or null
+     */
+    @Nullable
+    public Size getSize(@Nullable String key) {
+        unparcel();
+        final Object o = mMap.get(key);
+        try {
+            return (Size) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Size", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Size value, or null
+     */
+    @Nullable
+    public SizeF getSizeF(@Nullable String key) {
+        unparcel();
+        final Object o = mMap.get(key);
+        try {
+            return (SizeF) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "SizeF", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Bundle value, or null
+     */
+    @Nullable
+    public Bundle getBundle(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (Bundle) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Bundle", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if
+     * no mapping of the desired type exists for the given key or a {@code null}
+     * value is explicitly associated with the key.
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key a String, or {@code null}
+     * @return a Parcelable value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Nullable
+    public <T extends Parcelable> T getParcelable(@Nullable String key) {
+        unparcel();
+        Object o = getValue(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (T) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Parcelable", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #getParcelable(String)} instead.
+     *
+     * @param key a String, or {@code null}
+     * @param clazz The type of the object expected
+     * @return a Parcelable value, or {@code null}
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        return get(key, clazz);
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key a String, or {@code null}
+     * @return a Parcelable[] value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelableArray(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Nullable
+    public Parcelable[] getParcelableArray(@Nullable String key) {
+        unparcel();
+        Object o = getValue(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (Parcelable[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Parcelable[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #getParcelableArray(String)} instead.
+     *
+     * @param key a String, or {@code null}
+     * @param clazz The type of the items inside the array. This is only verified when unparceling.
+     * @return a Parcelable[] value, or {@code null}
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> T[] getParcelableArray(@Nullable String key, @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        unparcel();
+        try {
+            // In Java 12, we can pass clazz.arrayType() instead of Parcelable[] and later casting.
+            return (T[]) getValue(key, Parcelable[].class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, clazz.getCanonicalName() + "[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if
+     * no mapping of the desired type exists for the given key or a {@code null}
+     * value is explicitly associated with the key.
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key a String, or {@code null}
+     * @return an ArrayList<T> value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Nullable
+    public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
+        unparcel();
+        Object o = getValue(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (ArrayList<T>) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "ArrayList", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #getParcelableArrayList(String)} instead.
+     *
+     * @param key   a String, or {@code null}
+     * @param clazz The type of the items inside the array list. This is only verified when
+     *     unparceling.
+     * @return an ArrayList<T> value, or {@code null}
+     */
+    @SuppressLint("NullableCollection")
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> ArrayList<T> getParcelableArrayList(@Nullable String key,
+            @NonNull Class<? extends T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        return getArrayList(key, clazz);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a SparseArray of T values, or null
+     *
+     * @deprecated Use the type-safer {@link #getSparseParcelableArray(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Nullable
+    public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
+        unparcel();
+        Object o = getValue(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (SparseArray<T>) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "SparseArray", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #getSparseParcelableArray(String)} instead.
+     *
+     * @param key a String, or null
+     * @param clazz The type of the items inside the sparse array. This is only verified when
+     *     unparceling.
+     * @return a SparseArray of T values, or null
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> SparseArray<T> getSparseParcelableArray(@Nullable String key,
+            @NonNull Class<? extends T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        unparcel();
+        try {
+            return (SparseArray<T>) getValue(key, SparseArray.class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, "SparseArray<" + clazz.getCanonicalName() + ">", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Serializable value, or null
+     *
+     * @deprecated Use the type-safer {@link #getSerializable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Override
+    @Nullable
+    public Serializable getSerializable(@Nullable String key) {
+        return super.getSerializable(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * @param key   a String, or null
+     * @param clazz The expected class of the returned type
+     * @return a Serializable value, or null
+     */
+    @Nullable
+    public <T extends Serializable> T getSerializable(@Nullable String key,
+            @NonNull Class<T> clazz) {
+        return super.getSerializable(key, requireNonNull(clazz));
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<String> value, or null
+     */
+    @Override
+    @Nullable
+    public ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
+        return super.getIntegerArrayList(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<String> value, or null
+     */
+    @Override
+    @Nullable
+    public ArrayList<String> getStringArrayList(@Nullable String key) {
+        return super.getStringArrayList(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<CharSequence> value, or null
+     */
+    @Override
+    @Nullable
+    public ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
+        return super.getCharSequenceArrayList(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a byte[] value, or null
+     */
+    @Override
+    @Nullable
+    public byte[] getByteArray(@Nullable String key) {
+        return super.getByteArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a short[] value, or null
+     */
+    @Override
+    @Nullable
+    public short[] getShortArray(@Nullable String key) {
+        return super.getShortArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a char[] value, or null
+     */
+    @Override
+    @Nullable
+    public char[] getCharArray(@Nullable String key) {
+        return super.getCharArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a float[] value, or null
+     */
+    @Override
+    @Nullable
+    public float[] getFloatArray(@Nullable String key) {
+        return super.getFloatArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a CharSequence[] value, or null
+     */
+    @Override
+    @Nullable
+    public CharSequence[] getCharSequenceArray(@Nullable String key) {
+        return super.getCharSequenceArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an IBinder value, or null
+     */
+    @Nullable
+    public IBinder getBinder(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (IBinder) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "IBinder", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an IBinder value, or null
+     *
+     * @deprecated
+     * @hide This is the old name of the function.
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    @Nullable
+    public IBinder getIBinder(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (IBinder) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "IBinder", e);
+            return null;
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Bundle> CREATOR =
+        new Parcelable.Creator<Bundle>() {
+        @Override
+        public Bundle createFromParcel(Parcel in) {
+            return in.readBundle();
+        }
+
+        @Override
+        public Bundle[] newArray(int size) {
+            return new Bundle[size];
+        }
+    };
+
+    /**
+     * Report the nature of this Parcelable's contents
+     */
+    @Override
+    public int describeContents() {
+        int mask = 0;
+        if (hasFileDescriptors()) {
+            mask |= Parcelable.CONTENTS_FILE_DESCRIPTOR;
+        }
+        return mask;
+    }
+
+    /**
+     * Writes the Bundle contents to a Parcel, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to copy this bundle to.
+     */
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
+        try {
+            writeToParcelInner(parcel, flags);
+        } finally {
+            parcel.restoreAllowFds(oldAllowFds);
+        }
+    }
+
+    /**
+     * Reads the Parcel contents into this Bundle, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to overwrite this bundle from.
+     */
+    public void readFromParcel(Parcel parcel) {
+        readFromParcelInner(parcel);
+        mFlags = FLAG_ALLOW_FDS;
+        maybePrefillHasFds();
+    }
+
+    /**
+     * Returns a string representation of the {@link Bundle} that may be suitable for debugging. It
+     * won't print the internal map if its content hasn't been unparcelled.
+     */
+    @Override
+    public synchronized String toString() {
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                return "Bundle[EMPTY_PARCEL]";
+            } else {
+                return "Bundle[mParcelledData.dataSize=" +
+                        mParcelledData.dataSize() + "]";
+            }
+        }
+        return "Bundle[" + mMap.toString() + "]";
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized String toShortString() {
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                return "EMPTY_PARCEL";
+            } else {
+                return "mParcelledData.dataSize=" + mParcelledData.dataSize();
+            }
+        }
+        return mMap.toString();
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                proto.write(BundleProto.PARCELLED_DATA_SIZE, 0);
+            } else {
+                proto.write(BundleProto.PARCELLED_DATA_SIZE, mParcelledData.dataSize());
+            }
+        } else {
+            proto.write(BundleProto.MAP_DATA, mMap.toString());
+        }
+
+        proto.end(token);
+    }
+}
diff --git a/android-34/android/os/BundleMerger.java b/android-34/android/os/BundleMerger.java
new file mode 100644
index 0000000..857aaf5
--- /dev/null
+++ b/android-34/android/os/BundleMerger.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.function.BinaryOperator;
+
+/**
+ * Configured rules for merging two {@link Bundle} instances.
+ * <p>
+ * By default, values from both {@link Bundle} instances are blended together on
+ * a key-wise basis, and conflicting value definitions for a key are dropped.
+ * <p>
+ * Nuanced strategies for handling conflicting value definitions can be applied
+ * using {@link #setMergeStrategy(String, int)} and
+ * {@link #setDefaultMergeStrategy(int)}.
+ * <p>
+ * When conflicting values have <em>inconsistent</em> data types (such as trying
+ * to merge a {@link String} and a {@link Integer}), both conflicting values are
+ * rejected and the key becomes undefined, regardless of the requested strategy.
+ *
+ * @hide
+ */
+public class BundleMerger implements Parcelable {
+    private static final String TAG = "BundleMerger";
+
+    private @Strategy int mDefaultStrategy = STRATEGY_REJECT;
+
+    private final ArrayMap<String, Integer> mStrategies = new ArrayMap<>();
+
+    /**
+     * Merge strategy that rejects both conflicting values.
+     */
+    public static final int STRATEGY_REJECT = 0;
+
+    /**
+     * Merge strategy that selects the first of conflicting values.
+     */
+    public static final int STRATEGY_FIRST = 1;
+
+    /**
+     * Merge strategy that selects the last of conflicting values.
+     */
+    public static final int STRATEGY_LAST = 2;
+
+    /**
+     * Merge strategy that selects the "minimum" of conflicting values which are
+     * {@link Comparable} with each other.
+     */
+    public static final int STRATEGY_COMPARABLE_MIN = 3;
+
+    /**
+     * Merge strategy that selects the "maximum" of conflicting values which are
+     * {@link Comparable} with each other.
+     */
+    public static final int STRATEGY_COMPARABLE_MAX = 4;
+
+    /**
+     * Merge strategy that numerically adds both conflicting values.
+     */
+    public static final int STRATEGY_NUMBER_ADD = 10;
+
+    /**
+     * Merge strategy that numerically increments the first conflicting value by
+     * {@code 1} and ignores the last conflicting value.
+     */
+    public static final int STRATEGY_NUMBER_INCREMENT_FIRST = 20;
+
+    /**
+     * Merge strategy that numerically increments the first conflicting value by
+     * {@code 1} and also numerically adds both conflicting values.
+     */
+    public static final int STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD = 25;
+
+    /**
+     * Merge strategy that combines conflicting values using a boolean "and"
+     * operation.
+     */
+    public static final int STRATEGY_BOOLEAN_AND = 30;
+
+    /**
+     * Merge strategy that combines conflicting values using a boolean "or"
+     * operation.
+     */
+    public static final int STRATEGY_BOOLEAN_OR = 40;
+
+    /**
+     * Merge strategy that combines two conflicting array values by appending
+     * the last array after the first array.
+     */
+    public static final int STRATEGY_ARRAY_APPEND = 50;
+
+    /**
+     * Merge strategy that combines two conflicting {@link ArrayList} values by
+     * appending the last {@link ArrayList} after the first {@link ArrayList}.
+     */
+    public static final int STRATEGY_ARRAY_LIST_APPEND = 60;
+
+    @IntDef(flag = false, prefix = { "STRATEGY_" }, value = {
+            STRATEGY_REJECT,
+            STRATEGY_FIRST,
+            STRATEGY_LAST,
+            STRATEGY_COMPARABLE_MIN,
+            STRATEGY_COMPARABLE_MAX,
+            STRATEGY_NUMBER_ADD,
+            STRATEGY_NUMBER_INCREMENT_FIRST,
+            STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD,
+            STRATEGY_BOOLEAN_AND,
+            STRATEGY_BOOLEAN_OR,
+            STRATEGY_ARRAY_APPEND,
+            STRATEGY_ARRAY_LIST_APPEND,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Strategy {}
+
+    /**
+     * Create a empty set of rules for merging two {@link Bundle} instances.
+     */
+    public BundleMerger() {
+    }
+
+    private BundleMerger(@NonNull Parcel in) {
+        mDefaultStrategy = in.readInt();
+        final int N = in.readInt();
+        for (int i = 0; i < N; i++) {
+            mStrategies.put(in.readString(), in.readInt());
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mDefaultStrategy);
+        final int N = mStrategies.size();
+        out.writeInt(N);
+        for (int i = 0; i < N; i++) {
+            out.writeString(mStrategies.keyAt(i));
+            out.writeInt(mStrategies.valueAt(i));
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Configure the default merge strategy to be used when there isn't a
+     * more-specific strategy defined for a particular key via
+     * {@link #setMergeStrategy(String, int)}.
+     */
+    public void setDefaultMergeStrategy(@Strategy int strategy) {
+        mDefaultStrategy = strategy;
+    }
+
+    /**
+     * Configure the merge strategy to be used for the given key.
+     * <p>
+     * Subsequent calls for the same key will overwrite any previously
+     * configured strategy.
+     */
+    public void setMergeStrategy(@NonNull String key, @Strategy int strategy) {
+        mStrategies.put(key, strategy);
+    }
+
+    /**
+     * Return the merge strategy to be used for the given key, as defined by
+     * {@link #setMergeStrategy(String, int)}.
+     * <p>
+     * If no specific strategy has been configured for the given key, this
+     * returns {@link #setDefaultMergeStrategy(int)}.
+     */
+    public @Strategy int getMergeStrategy(@NonNull String key) {
+        return (int) mStrategies.getOrDefault(key, mDefaultStrategy);
+    }
+
+    /**
+     * Return a {@link BinaryOperator} which applies the strategies configured
+     * in this object to merge the two given {@link Bundle} arguments.
+     */
+    public BinaryOperator<Bundle> asBinaryOperator() {
+        return this::merge;
+    }
+
+    /**
+     * Apply the strategies configured in this object to merge the two given
+     * {@link Bundle} arguments.
+     *
+     * @return the merged {@link Bundle} result. If one argument is {@code null}
+     *         it will return the other argument. If both arguments are null it
+     *         will return {@code null}.
+     */
+    @SuppressWarnings("deprecation")
+    public @Nullable Bundle merge(@Nullable Bundle first, @Nullable Bundle last) {
+        if (first == null && last == null) {
+            return null;
+        }
+        if (first == null) {
+            first = Bundle.EMPTY;
+        }
+        if (last == null) {
+            last = Bundle.EMPTY;
+        }
+
+        // Start by bulk-copying all values without attempting to unpack any
+        // custom parcelables; we'll circle back to handle conflicts below
+        final Bundle res = new Bundle();
+        res.putAll(first);
+        res.putAll(last);
+
+        final ArraySet<String> conflictingKeys = new ArraySet<>();
+        conflictingKeys.addAll(first.keySet());
+        conflictingKeys.retainAll(last.keySet());
+        for (int i = 0; i < conflictingKeys.size(); i++) {
+            final String key = conflictingKeys.valueAt(i);
+            final int strategy = getMergeStrategy(key);
+            final Object firstValue = first.get(key);
+            final Object lastValue = last.get(key);
+            try {
+                res.putObject(key, merge(strategy, firstValue, lastValue));
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to merge key " + key + " with " + firstValue + " and "
+                        + lastValue + " using strategy " + strategy, e);
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Merge the two given values. If only one of the values is defined, it
+     * always wins, otherwise the given strategy is applied.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static @Nullable Object merge(@Strategy int strategy,
+            @Nullable Object first, @Nullable Object last) {
+        if (first == null) return last;
+        if (last == null) return first;
+
+        if (first.getClass() != last.getClass()) {
+            throw new IllegalArgumentException("Merging requires consistent classes; first "
+                    + first.getClass() + " last " + last.getClass());
+        }
+
+        switch (strategy) {
+            case STRATEGY_REJECT:
+                // Only actually reject when the values are different
+                if (Objects.deepEquals(first, last)) {
+                    return first;
+                } else {
+                    return null;
+                }
+            case STRATEGY_FIRST:
+                return first;
+            case STRATEGY_LAST:
+                return last;
+            case STRATEGY_COMPARABLE_MIN:
+                return comparableMin(first, last);
+            case STRATEGY_COMPARABLE_MAX:
+                return comparableMax(first, last);
+            case STRATEGY_NUMBER_ADD:
+                return numberAdd(first, last);
+            case STRATEGY_NUMBER_INCREMENT_FIRST:
+                return numberIncrementFirst(first, last);
+            case STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD:
+                return numberAdd(numberIncrementFirst(first, last), last);
+            case STRATEGY_BOOLEAN_AND:
+                return booleanAnd(first, last);
+            case STRATEGY_BOOLEAN_OR:
+                return booleanOr(first, last);
+            case STRATEGY_ARRAY_APPEND:
+                return arrayAppend(first, last);
+            case STRATEGY_ARRAY_LIST_APPEND:
+                return arrayListAppend(first, last);
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static @NonNull Object comparableMin(@NonNull Object first, @NonNull Object last) {
+        return ((Comparable<Object>) first).compareTo(last) < 0 ? first : last;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static @NonNull Object comparableMax(@NonNull Object first, @NonNull Object last) {
+        return ((Comparable<Object>) first).compareTo(last) >= 0 ? first : last;
+    }
+
+    private static @NonNull Object numberAdd(@NonNull Object first, @NonNull Object last) {
+        if (first instanceof Integer) {
+            return ((Integer) first) + ((Integer) last);
+        } else if (first instanceof Long) {
+            return ((Long) first) + ((Long) last);
+        } else if (first instanceof Float) {
+            return ((Float) first) + ((Float) last);
+        } else if (first instanceof Double) {
+            return ((Double) first) + ((Double) last);
+        } else {
+            throw new IllegalArgumentException("Unable to add " + first.getClass());
+        }
+    }
+
+    private static @NonNull Number numberIncrementFirst(@NonNull Object first,
+            @NonNull Object last) {
+        if (first instanceof Integer) {
+            return ((Integer) first) + 1;
+        } else if (first instanceof Long) {
+            return ((Long) first) + 1L;
+        } else {
+            throw new IllegalArgumentException("Unable to add " + first.getClass());
+        }
+    }
+
+    private static @NonNull Object booleanAnd(@NonNull Object first, @NonNull Object last) {
+        return ((Boolean) first) && ((Boolean) last);
+    }
+
+    private static @NonNull Object booleanOr(@NonNull Object first, @NonNull Object last) {
+        return ((Boolean) first) || ((Boolean) last);
+    }
+
+    private static @NonNull Object arrayAppend(@NonNull Object first, @NonNull Object last) {
+        if (!first.getClass().isArray()) {
+            throw new IllegalArgumentException("Unable to append " + first.getClass());
+        }
+        final Class<?> clazz = first.getClass().getComponentType();
+        final int firstLength = Array.getLength(first);
+        final int lastLength = Array.getLength(last);
+        final Object res = Array.newInstance(clazz, firstLength + lastLength);
+        System.arraycopy(first, 0, res, 0, firstLength);
+        System.arraycopy(last, 0, res, firstLength, lastLength);
+        return res;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static @NonNull Object arrayListAppend(@NonNull Object first, @NonNull Object last) {
+        if (!(first instanceof ArrayList)) {
+            throw new IllegalArgumentException("Unable to append " + first.getClass());
+        }
+        final ArrayList<Object> firstList = (ArrayList<Object>) first;
+        final ArrayList<Object> lastList = (ArrayList<Object>) last;
+        final ArrayList<Object> res = new ArrayList<>(firstList.size() + lastList.size());
+        res.addAll(firstList);
+        res.addAll(lastList);
+        return res;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<BundleMerger> CREATOR =
+            new Parcelable.Creator<BundleMerger>() {
+                @Override
+                public BundleMerger createFromParcel(Parcel in) {
+                    return new BundleMerger(in);
+                }
+
+                @Override
+                public BundleMerger[] newArray(int size) {
+                    return new BundleMerger[size];
+                }
+            };
+}
diff --git a/android-34/android/os/CancellationSignal.java b/android-34/android/os/CancellationSignal.java
new file mode 100644
index 0000000..260f7ad
--- /dev/null
+++ b/android-34/android/os/CancellationSignal.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+/**
+ * Provides the ability to cancel an operation in progress.
+ */
+public final class CancellationSignal {
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private boolean mIsCanceled;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private OnCancelListener mOnCancelListener;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private ICancellationSignal mRemote;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private boolean mCancelInProgress;
+
+    /**
+     * Creates a cancellation signal, initially not canceled.
+     */
+    public CancellationSignal() {
+    }
+
+    /**
+     * Returns true if the operation has been canceled.
+     *
+     * @return True if the operation has been canceled.
+     */
+    public boolean isCanceled() {
+        synchronized (this) {
+            return mIsCanceled;
+        }
+    }
+
+    /**
+     * Throws {@link OperationCanceledException} if the operation has been canceled.
+     *
+     * @throws OperationCanceledException if the operation has been canceled.
+     */
+    public void throwIfCanceled() {
+        if (isCanceled()) {
+            throw new OperationCanceledException();
+        }
+    }
+
+    /**
+     * Cancels the operation and signals the cancellation listener.
+     * If the operation has not yet started, then it will be canceled as soon as it does.
+     */
+    public void cancel() {
+        final OnCancelListener listener;
+        final ICancellationSignal remote;
+        synchronized (this) {
+            if (mIsCanceled) {
+                return;
+            }
+            mIsCanceled = true;
+            mCancelInProgress = true;
+            listener = mOnCancelListener;
+            remote = mRemote;
+        }
+
+        try {
+            if (listener != null) {
+                listener.onCancel();
+            }
+            if (remote != null) {
+                try {
+                    remote.cancel();
+                } catch (RemoteException ex) {
+                }
+            }
+        } finally {
+            synchronized (this) {
+                mCancelInProgress = false;
+                notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Sets the cancellation listener to be called when canceled.
+     *
+     * This method is intended to be used by the recipient of a cancellation signal
+     * such as a database or a content provider to handle cancellation requests
+     * while performing a long-running operation.  This method is not intended to be
+     * used by applications themselves.
+     *
+     * If {@link CancellationSignal#cancel} has already been called, then the provided
+     * listener is invoked immediately.
+     *
+     * This method is guaranteed that the listener will not be called after it
+     * has been removed.
+     *
+     * @param listener The cancellation listener, or null to remove the current listener.
+     */
+    public void setOnCancelListener(OnCancelListener listener) {
+        synchronized (this) {
+            waitForCancelFinishedLocked();
+
+            if (mOnCancelListener == listener) {
+                return;
+            }
+            mOnCancelListener = listener;
+            if (!mIsCanceled || listener == null) {
+                return;
+            }
+        }
+        listener.onCancel();
+    }
+
+    /**
+     * Sets the remote transport.
+     *
+     * If {@link CancellationSignal#cancel} has already been called, then the provided
+     * remote transport is canceled immediately.
+     *
+     * This method is guaranteed that the remote transport will not be called after it
+     * has been removed.
+     *
+     * @param remote The remote transport, or null to remove.
+     *
+     * @hide
+     */
+    public void setRemote(ICancellationSignal remote) {
+        synchronized (this) {
+            waitForCancelFinishedLocked();
+
+            if (mRemote == remote) {
+                return;
+            }
+            mRemote = remote;
+            if (!mIsCanceled || remote == null) {
+                return;
+            }
+        }
+        try {
+            remote.cancel();
+        } catch (RemoteException ex) {
+        }
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private void waitForCancelFinishedLocked() {
+        while (mCancelInProgress) {
+            try {
+                wait();
+            } catch (InterruptedException ex) {
+            }
+        }
+    }
+
+    /**
+     * Creates a transport that can be returned back to the caller of
+     * a Binder function and subsequently used to dispatch a cancellation signal.
+     *
+     * @return The new cancellation signal transport.
+     *
+     * @hide
+     */
+    public static ICancellationSignal createTransport() {
+        return new Transport();
+    }
+
+    /**
+     * Given a locally created transport, returns its associated cancellation signal.
+     *
+     * @param transport The locally created transport, or null if none.
+     * @return The associated cancellation signal, or null if none.
+     *
+     * @hide
+     */
+    public static CancellationSignal fromTransport(ICancellationSignal transport) {
+        if (transport instanceof Transport) {
+            return ((Transport)transport).mCancellationSignal;
+        }
+        return null;
+    }
+
+    /**
+     * Listens for cancellation.
+     */
+    public interface OnCancelListener {
+        /**
+         * Called when {@link CancellationSignal#cancel} is invoked.
+         */
+        void onCancel();
+    }
+
+    private static final class Transport extends ICancellationSignal.Stub {
+        final CancellationSignal mCancellationSignal = new CancellationSignal();
+
+        @Override
+        public void cancel() throws RemoteException {
+            mCancellationSignal.cancel();
+        }
+    }
+}
diff --git a/android-34/android/os/CancellationSignalBeamer.java b/android-34/android/os/CancellationSignalBeamer.java
new file mode 100644
index 0000000..5c0b221
--- /dev/null
+++ b/android-34/android/os/CancellationSignalBeamer.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.system.SystemCleaner;
+import android.util.Pair;
+import android.view.inputmethod.CancellableHandwritingGesture;
+import android.view.inputmethod.HandwritingGesture;
+
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A transport for {@link CancellationSignal}, but unlike
+ * {@link CancellationSignal#createTransport()} doesn't require pre-creating the transport in the
+ * target process. Instead, cancellation is forwarded over the same IPC surface as the cancellable
+ * request.
+ *
+ * <p><strong>Important:</strong> For this to work, the following invariants must be held up:
+ * <ul>
+ *     <li>A call to beam() <strong>MUST</strong> result in a call to close() on the result
+ *     (otherwise, the token will be leaked and cancellation isn't propagated), and that call
+ *     must happen after the call using the
+ *     token is sent (otherwise, any concurrent cancellation may be lost). It is strongly
+ *     recommended to use try-with-resources on the token.
+ *     <li>The cancel(), forget() and cancellable operations transporting the token must either
+ *     all be oneway on the same binder, or all be non-oneway to guarantee proper ordering.
+ *     <li>A {@link CancellationSignal} <strong>SHOULD</strong> be used only once, as there
+ *     can only be a single {@link android.os.CancellationSignal.OnCancelListener OnCancelListener}.
+ *
+ * </ul>
+ * <p>Caveats:
+ * <ul>
+ *     <li>Cancellation is only ever dispatched after the token is closed, and thus after the
+ *     call performing the cancellable operation (if the invariants are followed). The operation
+ *     must therefore not block the incoming binder thread, or cancellation won't be possible.
+ *     <li>Consequently, in the unlikely event that the sender dies right after beaming an already
+ *     cancelled {@link CancellationSignal}, the cancellation may be lost (unlike with
+ *     {@link CancellationSignal#createTransport()}).
+ *     <li>The forwarding OnCancelListener is set in the implied finally phase of try-with-resources
+ *         / when closing the token. If the receiver is in the same process, and the signal is
+ *         already cancelled, this may invoke the target's OnCancelListener during that phase.
+ * </ul>
+ *
+ *
+ * <p>Usage:
+ * <pre>
+ *  // Sender:
+ *
+ *  class FooManager {
+ *    var mCancellationSignalSender = new CancellationSignalBeamer.Sender() {
+ *      &#064;Override
+ *      public void onCancel(IBinder token) { remoteIFooService.onCancelToken(token); }
+ *
+ *      &#064;Override
+ *      public void onForget(IBinder token) { remoteIFooService.onForgetToken(token); }
+ *    };
+ *
+ *    public void doCancellableOperation(..., CancellationSignal cs) {
+ *      try (var csToken = mCancellationSignalSender.beam(cs)) {
+ *          remoteIFooService.doCancellableOperation(..., csToken);
+ *      }
+ *    }
+ *  }
+ *
+ *  // Receiver:
+ *
+ *  class FooManagerService extends IFooService.Stub {
+ *    var mCancellationSignalReceiver = new CancellationSignalBeamer.Receiver();
+ *
+ *    &#064;Override
+ *    public void doCancellableOperation(..., IBinder csToken) {
+ *      CancellationSignal cs = mCancellationSignalReceiver.unbeam(csToken))
+ *      // ...
+ *    }
+ *
+ *    &#064;Override
+ *    public void onCancelToken(..., IBinder csToken) {
+ *      mCancellationSignalReceiver.cancelToken(csToken))
+ *    }
+ *
+ *    &#064;Override
+ *    public void onForgetToken(..., IBinder csToken) {
+ *      mCancellationSignalReceiver.forgetToken(csToken))
+ *    }
+ *  }
+ *
+ * </pre>
+ *
+ * @hide
+ */
+public class CancellationSignalBeamer {
+
+    static final Cleaner sCleaner = SystemCleaner.cleaner();
+
+    /** The sending side of an {@link CancellationSignalBeamer} */
+    public abstract static class Sender {
+
+        /**
+         * Beams a {@link CancellationSignal} through an existing Binder interface.
+         *
+         * @param cs the {@code CancellationSignal} to beam, or {@code null}.
+         * @return an {@link IBinder} token. MUST be {@link CloseableToken#close}d <em>after</em>
+         *         the binder call transporting it to the remote process, best with
+         *         try-with-resources. {@code null} if {@code cs} was {@code null}.
+         */
+        // TODO(b/254888024): @MustBeClosed
+        @Nullable
+        public CloseableToken beam(@Nullable CancellationSignal cs) {
+            if (cs == null) {
+                return null;
+            }
+            return new Token(this, cs);
+        }
+
+        /**
+         * A {@link #beam}ed {@link CancellationSignal} was closed.
+         *
+         * MUST be forwarded to {@link Receiver#cancel} with proper ordering. See
+         * {@link CancellationSignalBeamer} for details.
+         */
+        public abstract void onCancel(@NonNull IBinder token);
+
+        /**
+         * A {@link #beam}ed {@link CancellationSignal} was GC'd.
+         *
+         * MUST be forwarded to {@link Receiver#forget} with proper ordering. See
+         * {@link CancellationSignalBeamer} for details.
+         */
+        public abstract void onForget(@NonNull IBinder token);
+
+        private static final ThreadLocal<Pair<Sender, ArrayList<CloseableToken>>> sScope =
+                new ThreadLocal<>();
+
+        /**
+         * Beams a {@link CancellationSignal} through an existing Binder interface.
+         * @param gesture {@link HandwritingGesture} that supports
+         *  {@link CancellableHandwritingGesture cancellation} requesting cancellation token.
+         * @return {@link IBinder} token. MUST be {@link MustClose#close}d <em>after</em>
+         *  the binder call transporting it to the remote process, best with
+         *  try-with-resources. {@code null} if {@code cs} was {@code null} or if
+         *  {@link HandwritingGesture} isn't {@link CancellableHandwritingGesture cancellable}.
+         */
+        @NonNull
+        public MustClose beamScopeIfNeeded(@NonNull HandwritingGesture gesture) {
+            if (!(gesture instanceof CancellableHandwritingGesture)) {
+                return null;
+            }
+            sScope.set(Pair.create(this, new ArrayList<>()));
+            return () -> {
+                var tokens = sScope.get().second;
+                sScope.remove();
+                for (int i = tokens.size() - 1; i >= 0; i--) {
+                    if (tokens.get(i) != null) {
+                        tokens.get(i).close();
+                    }
+                }
+            };
+        }
+
+        /**
+         * An {@link AutoCloseable} interface with {@link AutoCloseable#close()} callback.
+         */
+        public interface MustClose extends AutoCloseable {
+            @Override
+            void close();
+        }
+
+        /**
+         * Beams a {@link CancellationSignal} token from existing scope created by previous call to
+         * {@link #beamScopeIfNeeded()}
+         * @param cs {@link CancellationSignal} for which token should be returned.
+         * @return {@link IBinder} token.
+         */
+        @NonNull
+        public static IBinder beamFromScope(@NonNull CancellationSignal cs) {
+            var state = sScope.get();
+            if (state != null) {
+                var token = state.first.beam(cs);
+                state.second.add(token);
+                return token;
+            }
+            return null;
+        }
+
+        private static class Token extends Binder implements CloseableToken, Runnable {
+
+            private final Sender mSender;
+            private Preparer mPreparer;
+
+            private Token(Sender sender, CancellationSignal signal) {
+                mSender = sender;
+                mPreparer = new Preparer(sender, signal, this);
+            }
+
+            @Override
+            public void close() {
+                Preparer preparer = mPreparer;
+                mPreparer = null;
+                if (preparer != null) {
+                    preparer.setup();
+                }
+            }
+
+            @Override
+            public void run() {
+                mSender.onForget(this);
+            }
+
+            private static class Preparer implements CancellationSignal.OnCancelListener {
+                private final Sender mSender;
+                private final CancellationSignal mSignal;
+                private final Token mToken;
+
+                private Preparer(Sender sender, CancellationSignal signal, Token token) {
+                    mSender = sender;
+                    mSignal = signal;
+                    mToken = token;
+                }
+
+                void setup() {
+                    sCleaner.register(this, mToken);
+                    mSignal.setOnCancelListener(this);
+                }
+
+                @Override
+                public void onCancel() {
+                    try {
+                        mSender.onCancel(mToken);
+                    } finally {
+                        // Make sure we dispatch onCancel before the cleaner can run.
+                        Reference.reachabilityFence(this);
+                    }
+                }
+            }
+        }
+
+        /**
+         * A {@link #beam}ed {@link CancellationSignal} ready for sending over Binder.
+         *
+         * MUST be closed <em>after</em> it is sent over binder, ideally through try-with-resources.
+         */
+        public interface CloseableToken extends IBinder, MustClose {
+            @Override
+            void close(); // No throws
+        }
+    }
+
+    /** The receiving side of a {@link CancellationSignalBeamer}. */
+    public static class Receiver implements IBinder.DeathRecipient {
+        private final HashMap<IBinder, CancellationSignal> mTokenMap = new HashMap<>();
+        private final boolean mCancelOnSenderDeath;
+
+        /**
+         * Constructs a new {@code Receiver}.
+         *
+         * @param cancelOnSenderDeath if true, {@link CancellationSignal}s obtained from
+         *  {@link #unbeam} are automatically {@link #cancel}led if the sender token
+         *  {@link Binder#linkToDeath dies}; otherwise they are simnply dropped. Note: if the
+         *  sending process drops all references to the {@link CancellationSignal} before
+         *  process death, the cancellation is not guaranteed.
+         */
+        public Receiver(boolean cancelOnSenderDeath) {
+            mCancelOnSenderDeath = cancelOnSenderDeath;
+        }
+
+        /**
+         * Unbeams a token that was obtained via {@link Sender#beam} and turns it back into a
+         * {@link CancellationSignal}.
+         *
+         * A subsequent call to {@link #cancel} with the same token will cancel the returned
+         * {@code CancellationSignal}.
+         *
+         * @param token a token that was obtained from {@link Sender}, possibly in a remote process.
+         * @return a {@link CancellationSignal} linked to the given token.
+         */
+        @Nullable
+        @SuppressLint("VisiblySynchronized")
+        public CancellationSignal unbeam(@Nullable IBinder token) {
+            if (token == null) {
+                return null;
+            }
+            synchronized (this) {
+                CancellationSignal cs = mTokenMap.get(token);
+                if (cs != null) {
+                    return cs;
+                }
+
+                cs = new CancellationSignal();
+                mTokenMap.put(token, cs);
+                try {
+                    token.linkToDeath(this, 0);
+                } catch (RemoteException e) {
+                    dead(token);
+                }
+                return cs;
+            }
+        }
+
+        /**
+         * Forgets state associated with the given token (if any).
+         *
+         * Subsequent calls to {@link #cancel} or binder death notifications on the token will not
+         * have any effect.
+         *
+         * This MUST be invoked when forwarding {@link Sender#onForget}, otherwise the token and
+         * {@link CancellationSignal} will leak if the token was ever {@link #unbeam}ed.
+         *
+         * Optionally, the receiving service logic may also invoke this if it can guarantee that
+         * the unbeamed CancellationSignal isn't needed anymore (i.e. the cancellable operation
+         * using the CancellationSignal has been fully completed).
+         *
+         * @param token the token to forget. No-op if {@code null}.
+         */
+        @SuppressLint("VisiblySynchronized")
+        public void forget(@Nullable IBinder token) {
+            synchronized (this) {
+                if (mTokenMap.remove(token) != null) {
+                    token.unlinkToDeath(this, 0);
+                }
+            }
+        }
+
+        /**
+         * Cancels the {@link CancellationSignal} associated with the given token (if any).
+         *
+         * This MUST be invoked when forwarding {@link Sender#onCancel}, otherwise the token and
+         * {@link CancellationSignal} will leak if the token was ever {@link #unbeam}ed.
+         *
+         * Optionally, the receiving service logic may also invoke this if it can guarantee that
+         * the unbeamed CancellationSignal isn't needed anymore (i.e. the cancellable operation
+         * using the CancellationSignal has been fully completed).
+         *
+         * @param token the token to forget. No-op if {@code null}.
+         */
+        @SuppressLint("VisiblySynchronized")
+        public void cancel(@Nullable IBinder token) {
+            CancellationSignal cs;
+            synchronized (this) {
+                cs = mTokenMap.get(token);
+                if (cs != null) {
+                    forget(token);
+                } else {
+                    return;
+                }
+            }
+            cs.cancel();
+        }
+
+        private void dead(@NonNull IBinder token) {
+            if (mCancelOnSenderDeath) {
+                cancel(token);
+            } else {
+                forget(token);
+            }
+        }
+
+        @Override
+        public void binderDied(@NonNull IBinder who) {
+            dead(who);
+        }
+
+        @Override
+        public void binderDied() {
+            throw new RuntimeException("unreachable");
+        }
+    }
+}
diff --git a/android-34/android/os/CarrierAssociatedAppEntry.java b/android-34/android/os/CarrierAssociatedAppEntry.java
new file mode 100644
index 0000000..13f6eb6
--- /dev/null
+++ b/android-34/android/os/CarrierAssociatedAppEntry.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+/**
+ * Represents a carrier app entry for use with {@link SystemConfigService}.
+ *
+ * @hide
+ */
+public final class CarrierAssociatedAppEntry implements Parcelable {
+
+    /**
+     * For carrier-associated app entries that don't specify the addedInSdk XML
+     * attribute.
+     */
+    public static final int SDK_UNSPECIFIED = -1;
+
+    public final String packageName;
+    /** May be {@link #SDK_UNSPECIFIED}. */
+    public final int addedInSdk;
+
+    public CarrierAssociatedAppEntry(String packageName, int addedInSdk) {
+        this.packageName = packageName;
+        this.addedInSdk = addedInSdk;
+    }
+
+    public CarrierAssociatedAppEntry(Parcel in) {
+        packageName = in.readString();
+        addedInSdk = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(packageName);
+        dest.writeInt(addedInSdk);
+    }
+
+    public static final Parcelable.Creator<CarrierAssociatedAppEntry> CREATOR =
+            new Parcelable.Creator<CarrierAssociatedAppEntry>() {
+        @Override
+        public CarrierAssociatedAppEntry createFromParcel(Parcel source) {
+            return new CarrierAssociatedAppEntry(source);
+        }
+
+        @Override
+        public CarrierAssociatedAppEntry[] newArray(int size) {
+            return new CarrierAssociatedAppEntry[size];
+        }
+    };
+}
diff --git a/android-34/android/os/ChildZygoteProcess.java b/android-34/android/os/ChildZygoteProcess.java
new file mode 100644
index 0000000..337a3e2
--- /dev/null
+++ b/android-34/android/os/ChildZygoteProcess.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.net.LocalSocketAddress;
+
+/**
+ * Represents a connection to a child-zygote process. A child-zygote is spawend from another
+ * zygote process using {@link startChildZygote()}.
+ *
+ * {@hide}
+ */
+public class ChildZygoteProcess extends ZygoteProcess {
+    /**
+     * The PID of the child zygote process.
+     */
+    private final int mPid;
+
+    ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) {
+        super(socketAddress, null);
+        mPid = pid;
+    }
+
+    /**
+     * Returns the PID of the child-zygote process.
+     */
+    public int getPid() {
+        return mPid;
+    }
+}
diff --git a/android-34/android/os/CombinedVibration.java b/android-34/android/os/CombinedVibration.java
new file mode 100644
index 0000000..5f2c113
--- /dev/null
+++ b/android-34/android/os/CombinedVibration.java
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A CombinedVibration describes a combination of haptic effects to be performed by one or more
+ * {@link Vibrator Vibrators}.
+ *
+ * These effects may be any number of things, from single shot vibrations to complex waveforms.
+ *
+ * @see VibrationEffect
+ */
+@SuppressWarnings({"ParcelNotFinal", "ParcelCreator"}) // Parcel only extended here.
+public abstract class CombinedVibration implements Parcelable {
+    private static final int PARCEL_TOKEN_MONO = 1;
+    private static final int PARCEL_TOKEN_STEREO = 2;
+    private static final int PARCEL_TOKEN_SEQUENTIAL = 3;
+
+    /** Prevent subclassing from outside of the framework. */
+    CombinedVibration() {
+    }
+
+    /**
+     * Create a vibration that plays a single effect in parallel on all vibrators.
+     *
+     * A parallel vibration that takes a single {@link VibrationEffect} to be performed by multiple
+     * vibrators at the same time.
+     *
+     * @param effect The {@link VibrationEffect} to perform.
+     * @return The combined vibration representing the single effect to be played in all vibrators.
+     */
+    @NonNull
+    public static CombinedVibration createParallel(@NonNull VibrationEffect effect) {
+        CombinedVibration combined = new Mono(effect);
+        combined.validate();
+        return combined;
+    }
+
+    /**
+     * Start creating a vibration that plays effects in parallel on one or more vibrators.
+     *
+     * A parallel vibration takes one or more {@link VibrationEffect VibrationEffects} associated to
+     * individual vibrators to be performed at the same time.
+     *
+     * @see CombinedVibration.ParallelCombination
+     */
+    @NonNull
+    public static ParallelCombination startParallel() {
+        return new ParallelCombination();
+    }
+
+    /**
+     * Start creating a vibration that plays effects in sequence on one or more vibrators.
+     *
+     * A sequential vibration takes one or more {@link CombinedVibration CombinedVibrations} to be
+     * performed by one or more vibrators in order. Each {@link CombinedVibration} starts only after
+     * the previous one is finished.
+     *
+     * @hide
+     * @see CombinedVibration.SequentialCombination
+     */
+    @TestApi
+    @NonNull
+    public static SequentialCombination startSequential() {
+        return new SequentialCombination();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Gets the estimated duration of the combined vibration in milliseconds.
+     *
+     * <p>For parallel combinations this means the maximum duration of any individual {@link
+     * VibrationEffect}. For sequential combinations, this is a sum of each step and delays.
+     *
+     * <p>For combinations of effects without a defined end (e.g. a Waveform with a non-negative
+     * repeat index), this returns Long.MAX_VALUE. For effects with an unknown duration (e.g.
+     * Prebaked effects where the length is device and potentially run-time dependent), this returns
+     * -1.
+     *
+     * @hide
+     */
+    @TestApi
+    public abstract long getDuration();
+
+    /**
+     * Returns true if this effect could represent a touch haptic feedback.
+     *
+     * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
+     * for each vibration, with the correct usage. When a vibration is played with usage UNKNOWN,
+     * then this method will be used to classify the most common use case and make sure they are
+     * covered by the user settings for "Touch feedback".
+     *
+     * @hide
+     */
+    public boolean isHapticFeedbackCandidate() {
+        return false;
+    }
+
+    /** @hide */
+    public abstract void validate();
+
+    /** @hide */
+    public abstract boolean hasVibrator(int vibratorId);
+
+    /**
+     * A combination of haptic effects that should be played in multiple vibrators in parallel.
+     *
+     * @see CombinedVibration#startParallel()
+     */
+    public static final class ParallelCombination {
+
+        private final SparseArray<VibrationEffect> mEffects = new SparseArray<>();
+
+        ParallelCombination() {
+        }
+
+        /**
+         * Add or replace a one shot vibration effect to be performed by the specified vibrator.
+         *
+         * @param vibratorId The id of the vibrator that should perform this effect.
+         * @param effect     The effect this vibrator should play.
+         * @return The {@link ParallelCombination} object to enable adding
+         * multiple effects in one chain.
+         * @see VibrationEffect#createOneShot(long, int)
+         */
+        @NonNull
+        public ParallelCombination addVibrator(int vibratorId, @NonNull VibrationEffect effect) {
+            mEffects.put(vibratorId, effect);
+            return this;
+        }
+
+        /**
+         * Combine all of the added effects into a {@link CombinedVibration}.
+         *
+         * The {@link ParallelCombination} object is still valid after this
+         * call, so you can continue adding more effects to it and generating more
+         * {@link CombinedVibration}s by calling this method again.
+         *
+         * @return The {@link CombinedVibration} resulting from combining the added effects to
+         * be played in parallel.
+         */
+        @NonNull
+        public CombinedVibration combine() {
+            if (mEffects.size() == 0) {
+                throw new IllegalStateException(
+                        "Combination must have at least one element to combine.");
+            }
+            CombinedVibration combined = new Stereo(mEffects);
+            combined.validate();
+            return combined;
+        }
+    }
+
+    /**
+     * A combination of haptic effects that should be played in multiple vibrators in sequence.
+     *
+     * @hide
+     * @see CombinedVibration#startSequential()
+     */
+    @TestApi
+    public static final class SequentialCombination {
+
+        private final ArrayList<CombinedVibration> mEffects = new ArrayList<>();
+        private final ArrayList<Integer> mDelays = new ArrayList<>();
+
+        SequentialCombination() {
+        }
+
+        /**
+         * Add a single vibration effect to be performed next.
+         *
+         * Similar to {@link #addNext(int, VibrationEffect, int)}, but with no delay. The effect
+         * will start playing immediately after the previous vibration is finished.
+         *
+         * @param vibratorId The id of the vibrator that should perform this effect.
+         * @param effect     The effect this vibrator should play.
+         * @return The {@link CombinedVibration.SequentialCombination} object to enable adding
+         * multiple effects in one chain.
+         */
+        @NonNull
+        public SequentialCombination addNext(int vibratorId, @NonNull VibrationEffect effect) {
+            return addNext(vibratorId, effect, /* delay= */ 0);
+        }
+
+        /**
+         * Add a single vibration effect to be performed next.
+         *
+         * The delay is applied immediately after the previous vibration is finished. The effect
+         * will start playing after the delay.
+         *
+         * @param vibratorId The id of the vibrator that should perform this effect.
+         * @param effect     The effect this vibrator should play.
+         * @param delay      The amount of time, in milliseconds, to wait between playing the prior
+         *                   vibration and this one, starting at the time the previous vibration in
+         *                   this sequence is finished.
+         * @return The {@link CombinedVibration.SequentialCombination} object to enable adding
+         * multiple effects in one chain.
+         */
+        @NonNull
+        public SequentialCombination addNext(int vibratorId, @NonNull VibrationEffect effect,
+                int delay) {
+            return addNext(
+                    CombinedVibration.startParallel().addVibrator(vibratorId, effect).combine(),
+                    delay);
+        }
+
+        /**
+         * Add a combined vibration effect to be performed next.
+         *
+         * Similar to {@link #addNext(CombinedVibration, int)}, but with no delay. The effect will
+         * start playing immediately after the previous vibration is finished.
+         *
+         * @param effect The combined effect to be performed next.
+         * @return The {@link CombinedVibration.SequentialCombination} object to enable adding
+         * multiple effects in one chain.
+         * @see VibrationEffect#createOneShot(long, int)
+         */
+        @NonNull
+        public SequentialCombination addNext(@NonNull CombinedVibration effect) {
+            return addNext(effect, /* delay= */ 0);
+        }
+
+        /**
+         * Add a combined vibration effect to be performed next.
+         *
+         * The delay is applied immediately after the previous vibration is finished. The vibration
+         * will start playing after the delay.
+         *
+         * @param effect The combined effect to be performed next.
+         * @param delay  The amount of time, in milliseconds, to wait between playing the prior
+         *               vibration and this one, starting at the time the previous vibration in this
+         *               sequence is finished.
+         * @return The {@link CombinedVibration.SequentialCombination} object to enable adding
+         * multiple effects in one chain.
+         */
+        @NonNull
+        public SequentialCombination addNext(@NonNull CombinedVibration effect, int delay) {
+            if (effect instanceof Sequential) {
+                Sequential sequentialEffect = (Sequential) effect;
+                int firstEffectIndex = mDelays.size();
+                mEffects.addAll(sequentialEffect.getEffects());
+                mDelays.addAll(sequentialEffect.getDelays());
+                mDelays.set(firstEffectIndex, delay + mDelays.get(firstEffectIndex));
+            } else {
+                mEffects.add(effect);
+                mDelays.add(delay);
+            }
+            return this;
+        }
+
+        /**
+         * Combine all of the added effects in sequence.
+         *
+         * The {@link CombinedVibration.SequentialCombination} object is still valid after
+         * this call, so you can continue adding more effects to it and generating more {@link
+         * CombinedVibration}s by calling this method again.
+         *
+         * @return The {@link CombinedVibration} resulting from combining the added effects to
+         * be played in sequence.
+         */
+        @NonNull
+        public CombinedVibration combine() {
+            if (mEffects.size() == 0) {
+                throw new IllegalStateException(
+                        "Combination must have at least one element to combine.");
+            }
+            CombinedVibration combined = new Sequential(mEffects, mDelays);
+            combined.validate();
+            return combined;
+        }
+    }
+
+    /**
+     * Represents a single {@link VibrationEffect} that should be played in all vibrators at the
+     * same time.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class Mono extends CombinedVibration {
+        private final VibrationEffect mEffect;
+
+        Mono(Parcel in) {
+            mEffect = VibrationEffect.CREATOR.createFromParcel(in);
+        }
+
+        Mono(@NonNull VibrationEffect effect) {
+            mEffect = effect;
+        }
+
+        @NonNull
+        public VibrationEffect getEffect() {
+            return mEffect;
+        }
+
+        @Override
+        public long getDuration() {
+            return mEffect.getDuration();
+        }
+
+        /** @hide */
+        @Override
+        public boolean isHapticFeedbackCandidate() {
+            return mEffect.isHapticFeedbackCandidate();
+        }
+
+        /** @hide */
+        @Override
+        public void validate() {
+            mEffect.validate();
+        }
+
+        /** @hide */
+        @Override
+        public boolean hasVibrator(int vibratorId) {
+            return true;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Mono)) {
+                return false;
+            }
+            Mono other = (Mono) o;
+            return mEffect.equals(other.mEffect);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mEffect);
+        }
+
+        @Override
+        public String toString() {
+            return "Mono{mEffect=" + mEffect + '}';
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(PARCEL_TOKEN_MONO);
+            mEffect.writeToParcel(out, flags);
+        }
+
+        @NonNull
+        public static final Parcelable.Creator<Mono> CREATOR =
+                new Parcelable.Creator<Mono>() {
+                    @Override
+                    public Mono createFromParcel(@NonNull Parcel in) {
+                        // Skip the type token
+                        in.readInt();
+                        return new Mono(in);
+                    }
+
+                    @Override
+                    @NonNull
+                    public Mono[] newArray(int size) {
+                        return new Mono[size];
+                    }
+                };
+    }
+
+    /**
+     * Represents a set of {@link VibrationEffect VibrationEffects} associated to individual
+     * vibrators that should be played at the same time.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class Stereo extends CombinedVibration {
+
+        /** Mapping vibrator ids to effects. */
+        private final SparseArray<VibrationEffect> mEffects;
+
+        Stereo(Parcel in) {
+            int size = in.readInt();
+            mEffects = new SparseArray<>(size);
+            for (int i = 0; i < size; i++) {
+                int vibratorId = in.readInt();
+                mEffects.put(vibratorId, VibrationEffect.CREATOR.createFromParcel(in));
+            }
+        }
+
+        Stereo(@NonNull SparseArray<VibrationEffect> effects) {
+            mEffects = new SparseArray<>(effects.size());
+            for (int i = 0; i < effects.size(); i++) {
+                mEffects.put(effects.keyAt(i), effects.valueAt(i));
+            }
+        }
+
+        /** Effects to be performed in parallel, where each key represents the vibrator id. */
+        @NonNull
+        public SparseArray<VibrationEffect> getEffects() {
+            return mEffects;
+        }
+
+        @Override
+        public long getDuration() {
+            long maxDuration = Long.MIN_VALUE;
+            boolean hasUnknownStep = false;
+            for (int i = 0; i < mEffects.size(); i++) {
+                long duration = mEffects.valueAt(i).getDuration();
+                if (duration == Long.MAX_VALUE) {
+                    // If any duration is repeating, this combination duration is also repeating.
+                    return duration;
+                }
+                maxDuration = Math.max(maxDuration, duration);
+                // If any step is unknown, this combination duration will also be unknown, unless
+                // any step is repeating. Repeating vibrations take precedence over non-repeating
+                // ones in the service, so continue looping to check for repeating steps.
+                hasUnknownStep |= duration < 0;
+            }
+            if (hasUnknownStep) {
+                // If any step is unknown, this combination duration is also unknown.
+                return -1;
+            }
+            return maxDuration;
+        }
+
+        /** @hide */
+        @Override
+        public boolean isHapticFeedbackCandidate() {
+            for (int i = 0; i < mEffects.size(); i++) {
+                if (!mEffects.valueAt(i).isHapticFeedbackCandidate()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /** @hide */
+        @Override
+        public void validate() {
+            Preconditions.checkArgument(mEffects.size() > 0,
+                    "There should be at least one effect set for a combined effect");
+            for (int i = 0; i < mEffects.size(); i++) {
+                mEffects.valueAt(i).validate();
+            }
+        }
+
+        /** @hide */
+        @Override
+        public boolean hasVibrator(int vibratorId) {
+            return mEffects.indexOfKey(vibratorId) >= 0;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Stereo)) {
+                return false;
+            }
+            Stereo other = (Stereo) o;
+            if (mEffects.size() != other.mEffects.size()) {
+                return false;
+            }
+            for (int i = 0; i < mEffects.size(); i++) {
+                if (!mEffects.valueAt(i).equals(other.mEffects.get(mEffects.keyAt(i)))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return mEffects.contentHashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "Stereo{mEffects=" + mEffects + '}';
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(PARCEL_TOKEN_STEREO);
+            out.writeInt(mEffects.size());
+            for (int i = 0; i < mEffects.size(); i++) {
+                out.writeInt(mEffects.keyAt(i));
+                mEffects.valueAt(i).writeToParcel(out, flags);
+            }
+        }
+
+        @NonNull
+        public static final Parcelable.Creator<Stereo> CREATOR =
+                new Parcelable.Creator<Stereo>() {
+                    @Override
+                    public Stereo createFromParcel(@NonNull Parcel in) {
+                        // Skip the type token
+                        in.readInt();
+                        return new Stereo(in);
+                    }
+
+                    @Override
+                    @NonNull
+                    public Stereo[] newArray(int size) {
+                        return new Stereo[size];
+                    }
+                };
+    }
+
+    /**
+     * Represents a list of {@link CombinedVibration CombinedVibrations} that should be played in
+     * sequence.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class Sequential extends CombinedVibration {
+        // If a vibration is playing more than 3 effects, it's probably not haptic feedback
+        private static final long MAX_HAPTIC_FEEDBACK_SEQUENCE_SIZE = 3;
+
+        private final List<CombinedVibration> mEffects;
+        private final List<Integer> mDelays;
+
+        Sequential(Parcel in) {
+            int size = in.readInt();
+            mEffects = new ArrayList<>(size);
+            mDelays = new ArrayList<>(size);
+            for (int i = 0; i < size; i++) {
+                mDelays.add(in.readInt());
+                mEffects.add(CombinedVibration.CREATOR.createFromParcel(in));
+            }
+        }
+
+        Sequential(@NonNull List<CombinedVibration> effects,
+                @NonNull List<Integer> delays) {
+            mEffects = new ArrayList<>(effects);
+            mDelays = new ArrayList<>(delays);
+        }
+
+        /** Effects to be performed in sequence. */
+        @NonNull
+        public List<CombinedVibration> getEffects() {
+            return mEffects;
+        }
+
+        /** Delay to be applied before each effect in {@link #getEffects()}. */
+        @NonNull
+        public List<Integer> getDelays() {
+            return mDelays;
+        }
+
+        @Override
+        public long getDuration() {
+            boolean hasUnknownStep = false;
+            long durations = 0;
+            final int effectCount = mEffects.size();
+            for (int i = 0; i < effectCount; i++) {
+                CombinedVibration effect = mEffects.get(i);
+                long duration = effect.getDuration();
+                if (duration == Long.MAX_VALUE) {
+                    // If any duration is repeating, this combination duration is also repeating.
+                    return duration;
+                }
+                durations += duration;
+                // If any step is unknown, this combination duration will also be unknown, unless
+                // any step is repeating. Repeating vibrations take precedence over non-repeating
+                // ones in the service, so continue looping to check for repeating steps.
+                hasUnknownStep |= duration < 0;
+            }
+            if (hasUnknownStep) {
+                // If any step is unknown, this combination duration is also unknown.
+                return -1;
+            }
+            long delays = 0;
+            for (int i = 0; i < effectCount; i++) {
+                delays += mDelays.get(i);
+            }
+            return durations + delays;
+        }
+
+        /** @hide */
+        @Override
+        public boolean isHapticFeedbackCandidate() {
+            final int effectCount = mEffects.size();
+            if (effectCount > MAX_HAPTIC_FEEDBACK_SEQUENCE_SIZE) {
+                return false;
+            }
+            for (int i = 0; i < effectCount; i++) {
+                if (!mEffects.get(i).isHapticFeedbackCandidate()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /** @hide */
+        @Override
+        public void validate() {
+            Preconditions.checkArgument(mEffects.size() > 0,
+                    "There should be at least one effect set for a combined effect");
+            Preconditions.checkArgument(mEffects.size() == mDelays.size(),
+                    "Effect and delays should have equal length");
+            final int effectCount = mEffects.size();
+            for (int i = 0; i < effectCount; i++) {
+                if (mDelays.get(i) < 0) {
+                    throw new IllegalArgumentException("Delays must all be >= 0"
+                            + " (delays=" + mDelays + ")");
+                }
+            }
+            for (int i = 0; i < effectCount; i++) {
+                CombinedVibration effect = mEffects.get(i);
+                if (effect instanceof Sequential) {
+                    throw new IllegalArgumentException(
+                            "There should be no nested sequential effects in a combined effect");
+                }
+                effect.validate();
+            }
+        }
+
+        /** @hide */
+        @Override
+        public boolean hasVibrator(int vibratorId) {
+            final int effectCount = mEffects.size();
+            for (int i = 0; i < effectCount; i++) {
+                if (mEffects.get(i).hasVibrator(vibratorId)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Sequential)) {
+                return false;
+            }
+            Sequential other = (Sequential) o;
+            return mDelays.equals(other.mDelays) && mEffects.equals(other.mEffects);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mEffects, mDelays);
+        }
+
+        @Override
+        public String toString() {
+            return "Sequential{mEffects=" + mEffects + ", mDelays=" + mDelays + '}';
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(PARCEL_TOKEN_SEQUENTIAL);
+            out.writeInt(mEffects.size());
+            for (int i = 0; i < mEffects.size(); i++) {
+                out.writeInt(mDelays.get(i));
+                mEffects.get(i).writeToParcel(out, flags);
+            }
+        }
+
+        @NonNull
+        public static final Parcelable.Creator<Sequential> CREATOR =
+                new Parcelable.Creator<Sequential>() {
+                    @Override
+                    public Sequential createFromParcel(@NonNull Parcel in) {
+                        // Skip the type token
+                        in.readInt();
+                        return new Sequential(in);
+                    }
+
+                    @Override
+                    @NonNull
+                    public Sequential[] newArray(int size) {
+                        return new Sequential[size];
+                    }
+                };
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<CombinedVibration> CREATOR =
+            new Parcelable.Creator<CombinedVibration>() {
+                @Override
+                public CombinedVibration createFromParcel(Parcel in) {
+                    int token = in.readInt();
+                    if (token == PARCEL_TOKEN_MONO) {
+                        return new Mono(in);
+                    } else if (token == PARCEL_TOKEN_STEREO) {
+                        return new Stereo(in);
+                    } else if (token == PARCEL_TOKEN_SEQUENTIAL) {
+                        return new Sequential(in);
+                    } else {
+                        throw new IllegalStateException(
+                                "Unexpected combined vibration event type token in parcel.");
+                    }
+                }
+
+                @Override
+                public CombinedVibration[] newArray(int size) {
+                    return new CombinedVibration[size];
+                }
+            };
+}
diff --git a/android-34/android/os/ConditionVariable.java b/android-34/android/os/ConditionVariable.java
new file mode 100644
index 0000000..a13eaa6
--- /dev/null
+++ b/android-34/android/os/ConditionVariable.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+/**
+ * Class that implements the condition variable locking paradigm.
+ *
+ * <p>
+ * This differs from the built-in java.lang.Object wait() and notify()
+ * in that this class contains the condition to wait on itself.  That means
+ * open(), close() and block() are sticky.  If open() is called before block(),
+ * block() will not block, and instead return immediately.
+ *
+ * <p>
+ * This class uses itself as the object to wait on, so if you wait()
+ * or notify() on a ConditionVariable, the results are undefined.
+ */
+public class ConditionVariable
+{
+    private volatile boolean mCondition;
+
+    /**
+     * Create the ConditionVariable in the default closed state.
+     */
+    public ConditionVariable()
+    {
+        mCondition = false;
+    }
+
+    /**
+     * Create the ConditionVariable with the given state.
+     * 
+     * <p>
+     * Pass true for opened and false for closed.
+     */
+    public ConditionVariable(boolean state)
+    {
+        mCondition = state;
+    }
+
+    /**
+     * Open the condition, and release all threads that are blocked.
+     *
+     * <p>
+     * Any threads that later approach block() will not block unless close()
+     * is called.
+     */
+    public void open()
+    {
+        synchronized (this) {
+            boolean old = mCondition;
+            mCondition = true;
+            if (!old) {
+                this.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Reset the condition to the closed state.
+     *
+     * <p>
+     * Any threads that call block() will block until someone calls open.
+     */
+    public void close()
+    {
+        synchronized (this) {
+            mCondition = false;
+        }
+    }
+
+    /**
+     * Block the current thread until the condition is opened.
+     *
+     * <p>
+     * If the condition is already opened, return immediately.
+     */
+    public void block()
+    {
+        synchronized (this) {
+            while (!mCondition) {
+                try {
+                    this.wait();
+                }
+                catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Block the current thread until the condition is opened or until
+     * timeoutMs milliseconds have passed.
+     *
+     * <p>
+     * If the condition is already opened, return immediately.
+     *
+     * @param timeoutMs the maximum time to wait in milliseconds.
+     *
+     * @return true if the condition was opened, false if the call returns
+     * because of the timeout.
+     */
+    public boolean block(long timeoutMs)
+    {
+        // Object.wait(0) means wait forever, to mimic this, we just
+        // call the other block() method in that case.  It simplifies
+        // this code for the common case.
+        if (timeoutMs != 0) {
+            synchronized (this) {
+                long now = SystemClock.elapsedRealtime();
+                long end = now + timeoutMs;
+                while (!mCondition && now < end) {
+                    try {
+                        this.wait(end-now);
+                    }
+                    catch (InterruptedException e) {
+                    }
+                    now = SystemClock.elapsedRealtime();
+                }
+                return mCondition;
+            }
+        } else {
+            this.block();
+            return true;
+        }
+    }
+}
diff --git a/android-34/android/os/ConfigUpdate.java b/android-34/android/os/ConfigUpdate.java
new file mode 100644
index 0000000..4908919
--- /dev/null
+++ b/android-34/android/os/ConfigUpdate.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.os;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+
+/**
+ * Intents used to provide unbundled updates of system data.
+ * All require the UPDATE_CONFIG permission.
+ *
+ * @see com.android.server.updates
+ * @hide
+ */
+@SystemApi
+public final class ConfigUpdate {
+
+    /**
+     * Update system wide certificate pins for TLS connections.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
+
+    /**
+     * Update system wide Intent firewall.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_INTENT_FIREWALL
+            = "android.intent.action.UPDATE_INTENT_FIREWALL";
+
+    /**
+     * Update list of permium SMS short codes.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_SMS_SHORT_CODES
+            = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+
+    /**
+     * Update list of carrier provisioning URLs.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS
+            = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
+
+    /**
+     * Update set of trusted logs used for Certificate Transparency support for TLS connections.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_CT_LOGS
+            = "android.intent.action.UPDATE_CT_LOGS";
+
+    /**
+     * Update language detection model file.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
+
+    /**
+     * Update smart selection model file.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_SMART_SELECTION
+            = "android.intent.action.UPDATE_SMART_SELECTION";
+
+    /**
+     * Update conversation actions model file.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_CONVERSATION_ACTIONS
+            = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
+
+    /**
+     * Update network watchlist config file.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_NETWORK_WATCHLIST
+            = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
+
+    /**
+     * Broadcast intent action indicating that the updated carrier id config is available.
+     * <p>Extra: "VERSION" the numeric version of the new data. Devices should only install if the
+     * update version is newer than the current one.
+     * <p>Extra: "REQUIRED_HASH" the hash of the current update data.
+     * <p>Input: {@link android.content.Intent#getData} is URI of downloaded carrier id file.
+     * Devices should pick up the downloaded file and persist to the database
+     * {@link com.android.providers.telephony.CarrierIdProvider}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_CARRIER_ID_DB
+            = "android.os.action.UPDATE_CARRIER_ID_DB";
+
+    /**
+    * Update the emergency number database into the devices.
+    * <p>Extra: {@link #EXTRA_VERSION} the numeric version of the database.
+    * <p>Extra: {@link #EXTRA_REQUIRED_HASH} hash of the database, which is encoded by base-16
+     * SHA512.
+    * <p>Input: {@link android.content.Intent#getData} the URI to download emergency number
+    * database.
+    *
+    * @hide
+    */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB =
+            "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
+
+    /**
+     * An integer to indicate the numeric version of the new data. Devices should only install
+     * if the update version is newer than the current one.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_VERSION = "android.os.extra.VERSION";
+
+    /**
+     * Hash of the database, which is encoded by base-16 SHA512.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH";
+
+    private ConfigUpdate() {
+    }
+}
diff --git a/android-34/android/os/CoolingDevice.java b/android-34/android/os/CoolingDevice.java
new file mode 100644
index 0000000..4ddcd9d
--- /dev/null
+++ b/android-34/android/os/CoolingDevice.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.thermal.CoolingType;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Cooling device values used by IThermalService.
+ *
+ * @hide
+ */
+public final class CoolingDevice implements Parcelable {
+    /**
+     * Current throttle state of the cooling device. The value can any unsigned integer
+     * numbers between 0 and max_state defined in its driver, usually representing the
+     * associated device's power state. 0 means device is not in throttling, higher value
+     * means deeper throttling.
+     */
+    private final long mValue;
+    /** A cooling device type from ThermalHAL */
+    private final int mType;
+    /** Name of this cooling device */
+    private final String mName;
+
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_FAN,
+            TYPE_BATTERY,
+            TYPE_CPU,
+            TYPE_GPU,
+            TYPE_MODEM,
+            TYPE_NPU,
+            TYPE_COMPONENT,
+            TYPE_TPU,
+            TYPE_POWER_AMPLIFIER,
+            TYPE_DISPLAY,
+            TYPE_SPEAKER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /** Keep in sync with hardware/interfaces/thermal/aidl/android/hardware/thermal
+     * /ThrottlingSeverity.aidl */
+    /** Fan for active cooling */
+    public static final int TYPE_FAN = CoolingType.FAN;
+    /** Battery charging cooling deivice */
+    public static final int TYPE_BATTERY = CoolingType.BATTERY;
+    /** CPU cooling deivice */
+    public static final int TYPE_CPU = CoolingType.CPU;
+    /** GPU cooling deivice */
+    public static final int TYPE_GPU = CoolingType.GPU;
+    /** Modem cooling deivice */
+    public static final int TYPE_MODEM = CoolingType.MODEM;
+    /** NPU cooling deivice */
+    public static final int TYPE_NPU = CoolingType.NPU;
+    /** Generic passive cooling deivice */
+    public static final int TYPE_COMPONENT = CoolingType.COMPONENT;
+    /** TPU cooling deivice */
+    public static final int TYPE_TPU = CoolingType.TPU;
+    /** Power amplifier cooling device */
+    public static final int TYPE_POWER_AMPLIFIER = CoolingType.POWER_AMPLIFIER;
+    /** Display cooling device */
+    public static final int TYPE_DISPLAY = CoolingType.DISPLAY;
+    /** Speaker cooling device */
+    public static final int TYPE_SPEAKER = CoolingType.SPEAKER;
+
+    /**
+     * Verify a valid cooling device type.
+     *
+     * @return true if a cooling device type is valid otherwise false.
+     */
+    public static boolean isValidType(@Type int type) {
+        return type >= TYPE_FAN && type <= TYPE_SPEAKER;
+    }
+
+    public CoolingDevice(long value, @Type int type, @NonNull String name) {
+        Preconditions.checkArgument(isValidType(type), "Invalid Type");
+        mValue = value;
+        mType = type;
+        mName = Preconditions.checkStringNotEmpty(name);
+    }
+
+    /**
+     * Return the cooling device value.
+     *
+     * @return a cooling device value in int.
+     */
+    public long getValue() {
+        return mValue;
+    }
+
+    /**
+     * Return the cooling device type.
+     *
+     * @return a cooling device type: TYPE_*
+     */
+    public @Type int getType() {
+        return mType;
+    }
+
+    /**
+     * Return the cooling device name.
+     *
+     * @return a cooling device name as String.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    @Override
+    public String toString() {
+        return "CoolingDevice{mValue=" + mValue + ", mType=" + mType + ", mName=" + mName + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mName.hashCode();
+        hash = 31 * hash + Long.hashCode(mValue);
+        hash = 31 * hash + mType;
+        return hash;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (!(o instanceof CoolingDevice)) {
+            return false;
+        }
+        CoolingDevice other = (CoolingDevice) o;
+        return other.mValue == mValue && other.mType == mType && other.mName.equals(mName);
+    }
+
+    @Override
+    public void writeToParcel(Parcel p, int flags) {
+        p.writeLong(mValue);
+        p.writeInt(mType);
+        p.writeString(mName);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<CoolingDevice> CREATOR =
+            new Parcelable.Creator<CoolingDevice>() {
+                @Override
+                public CoolingDevice createFromParcel(Parcel p) {
+                    long value = p.readLong();
+                    int type = p.readInt();
+                    String name = p.readString();
+                    return new CoolingDevice(value, type, name);
+                }
+
+                @Override
+                public CoolingDevice[] newArray(int size) {
+                    return new CoolingDevice[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-34/android/os/CountDownTimer.java b/android-34/android/os/CountDownTimer.java
new file mode 100644
index 0000000..51faa85
--- /dev/null
+++ b/android-34/android/os/CountDownTimer.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ * 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.os;
+
+/**
+ * Schedule a countdown until a time in the future, with
+ * regular notifications on intervals along the way.
+ *
+ * Example of showing a 30 second countdown in a text field:
+ *
+ * <div>
+ * <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
+ * <pre class="prettyprint lang-kotlin">
+ * object : CountDownTimer(30000, 1000) {
+ *
+ *     override fun onTick(millisUntilFinished: Long) {
+ *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
+ *     }
+ *
+ *     override fun onFinish() {
+ *         mTextField.setText("done!")
+ *     }
+ * }.start()
+ * </pre>
+ * </section><section><h3 id="java">Java</h3>
+ * <pre class="prettyprint lang-java">
+ * new CountDownTimer(30000, 1000) {
+ *
+ *     public void onTick(long millisUntilFinished) {
+ *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
+ *     }
+ *
+ *     public void onFinish() {
+ *         mTextField.setText("done!");
+ *     }
+ * }.start();
+ * </pre></section></div></div>
+ *
+ * The calls to {@link #onTick(long)} are synchronized to this object so that
+ * one call to {@link #onTick(long)} won't ever occur before the previous
+ * callback is complete.  This is only relevant when the implementation of
+ * {@link #onTick(long)} takes an amount of time to execute that is significant
+ * compared to the countdown interval.
+ */
+public abstract class CountDownTimer {
+
+    /**
+     * Millis since epoch when alarm should stop.
+     */
+    private final long mMillisInFuture;
+
+    /**
+     * The interval in millis that the user receives callbacks
+     */
+    private final long mCountdownInterval;
+
+    private long mStopTimeInFuture;
+    
+    /**
+    * boolean representing if the timer was cancelled
+    */
+    private boolean mCancelled = false;
+
+    /**
+     * @param millisInFuture The number of millis in the future from the call
+     *   to {@link #start()} until the countdown is done and {@link #onFinish()}
+     *   is called.
+     * @param countDownInterval The interval along the way to receive
+     *   {@link #onTick(long)} callbacks.
+     */
+    public CountDownTimer(long millisInFuture, long countDownInterval) {
+        mMillisInFuture = millisInFuture;
+        mCountdownInterval = countDownInterval;
+    }
+
+    /**
+     * Cancel the countdown.
+     */
+    public synchronized final void cancel() {
+        mCancelled = true;
+        mHandler.removeMessages(MSG);
+    }
+
+    /**
+     * Start the countdown.
+     */
+    public synchronized final CountDownTimer start() {
+        mCancelled = false;
+        if (mMillisInFuture <= 0) {
+            onFinish();
+            return this;
+        }
+        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
+        mHandler.sendMessage(mHandler.obtainMessage(MSG));
+        return this;
+    }
+
+
+    /**
+     * Callback fired on regular interval.
+     * @param millisUntilFinished The amount of time until finished.
+     */
+    public abstract void onTick(long millisUntilFinished);
+
+    /**
+     * Callback fired when the time is up.
+     */
+    public abstract void onFinish();
+
+
+    private static final int MSG = 1;
+
+
+    // handles counting down
+    private Handler mHandler = new Handler() {
+
+        @Override
+        public void handleMessage(Message msg) {
+
+            synchronized (CountDownTimer.this) {
+                if (mCancelled) {
+                    return;
+                }
+
+                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
+
+                if (millisLeft <= 0) {
+                    onFinish();
+                } else {
+                    long lastTickStart = SystemClock.elapsedRealtime();
+                    onTick(millisLeft);
+
+                    // take into account user's onTick taking time to execute
+                    long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
+                    long delay;
+
+                    if (millisLeft < mCountdownInterval) {
+                        // just delay until done
+                        delay = millisLeft - lastTickDuration;
+
+                        // special case: user's onTick took more than interval to
+                        // complete, trigger onFinish without delay
+                        if (delay < 0) delay = 0;
+                    } else {
+                        delay = mCountdownInterval - lastTickDuration;
+
+                        // special case: user's onTick took more than interval to
+                        // complete, skip to next interval
+                        while (delay < 0) delay += mCountdownInterval;
+                    }
+
+                    sendMessageDelayed(obtainMessage(MSG), delay);
+                }
+            }
+        }
+    };
+}
diff --git a/android-34/android/os/CpuUsageInfo.java b/android-34/android/os/CpuUsageInfo.java
new file mode 100644
index 0000000..444579f
--- /dev/null
+++ b/android-34/android/os/CpuUsageInfo.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+/**
+ * CPU usage information per core.
+ */
+public final class CpuUsageInfo implements Parcelable {
+    private long mActive;
+    private long mTotal;
+
+    public static final @android.annotation.NonNull Parcelable.Creator<CpuUsageInfo> CREATOR = new
+            Parcelable.Creator<CpuUsageInfo>() {
+                    public CpuUsageInfo createFromParcel(Parcel in) {
+                        return new CpuUsageInfo(in);
+                    }
+
+                    public CpuUsageInfo[] newArray(int size) {
+                        return new CpuUsageInfo[size];
+                    }
+                };
+
+    /** @hide */
+    public CpuUsageInfo(long activeTime, long totalTime) {
+        mActive = activeTime;
+        mTotal = totalTime;
+    }
+
+    private CpuUsageInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Gets the active time in milliseconds since the system last booted.
+     *
+     * @return Active time in milliseconds.
+     */
+    public long getActive() {
+        return mActive;
+    }
+
+    /**
+     * Gets the total time in milliseconds that the CPU has been enabled since the system last
+     * booted. This includes time the CPU spent idle.
+     *
+     * @return Total time in milliseconds.
+     */
+    public long getTotal() {
+        return mTotal;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(mActive);
+        out.writeLong(mTotal);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mActive = in.readLong();
+        mTotal = in.readLong();
+    }
+}
diff --git a/android-34/android/os/CpuUsageTrackingPerfTest.java b/android-34/android/os/CpuUsageTrackingPerfTest.java
new file mode 100644
index 0000000..0d7b7ca
--- /dev/null
+++ b/android-34/android/os/CpuUsageTrackingPerfTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * Performance tests collecting CPU data different mechanisms.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class CpuUsageTrackingPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeSystemThread() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Binder b = new Binder();
+        while (state.keepRunning()) {
+            SystemClock.currentThreadTimeMicro();
+        }
+    }
+
+    @Test
+    public void timeReadStatFileDirectly() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        // CPU usage by frequency for this pid. Data is in text format.
+        final String procFile = "/proc/self/stat";
+        while (state.keepRunning()) {
+            byte[] data = Files.readAllBytes(Paths.get(procFile));
+        }
+    }
+
+    @Test
+    public void timeReadPidProcDirectly() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        // CPU usage by frequency for this pid. Data is in text format.
+        final String procFile = "/proc/self/time_in_state";
+        while (state.keepRunning()) {
+            byte[] data = Files.readAllBytes(Paths.get(procFile));
+        }
+    }
+
+    @Test
+    public void timeReadThreadProcDirectly() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        // CPU usage by frequency for this UID. Data is in text format.
+        final String procFile = "/proc/self/task/" + android.os.Process.myTid()
+                + "/time_in_state";
+        while (state.keepRunning()) {
+            byte[] data = Files.readAllBytes(Paths.get(procFile));
+        }
+    }
+}
diff --git a/android-34/android/os/DeadObjectException.java b/android-34/android/os/DeadObjectException.java
new file mode 100644
index 0000000..e06b0f9
--- /dev/null
+++ b/android-34/android/os/DeadObjectException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2006 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.os;
+import android.os.RemoteException;
+
+/**
+ * The object you are calling has died, because its hosting process
+ * no longer exists.
+ */
+public class DeadObjectException extends RemoteException {
+    public DeadObjectException() {
+        super();
+    }
+
+    public DeadObjectException(String message) {
+        super(message);
+    }
+}
diff --git a/android-34/android/os/DeadSystemException.java b/android-34/android/os/DeadSystemException.java
new file mode 100644
index 0000000..8fb53e2
--- /dev/null
+++ b/android-34/android/os/DeadSystemException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.os;
+
+/**
+ * The core Android system has died and is going through a runtime restart. All
+ * running apps will be promptly killed.
+ */
+public class DeadSystemException extends DeadObjectException {
+    public DeadSystemException() {
+        super();
+    }
+}
diff --git a/android-34/android/os/DeadSystemRuntimeException.java b/android-34/android/os/DeadSystemRuntimeException.java
new file mode 100644
index 0000000..1e86924
--- /dev/null
+++ b/android-34/android/os/DeadSystemRuntimeException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+/**
+ * Exception thrown when a call into system_server resulted in a
+ * DeadObjectException, meaning that the system_server has died.  There's
+ * nothing apps can do at this point - the system will automatically restart -
+ * so there's no point in catching this.
+ *
+ * @hide
+ */
+public class DeadSystemRuntimeException extends RuntimeException {
+    public DeadSystemRuntimeException() {
+        super(new DeadSystemException());
+    }
+}
diff --git a/android-34/android/os/Debug.java b/android-34/android/os/Debug.java
new file mode 100644
index 0000000..62d9c69
--- /dev/null
+++ b/android-34/android/os/Debug.java
@@ -0,0 +1,2687 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.TypedProperties;
+
+import dalvik.system.VMDebug;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Provides various debugging methods for Android applications, including
+ * tracing and allocation counts.
+ * <p><strong>Logging Trace Files</strong></p>
+ * <p>Debug can create log files that give details about an application, such as
+ * a call stack and start/stop times for any running methods. See <a
+ * href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs with
+ * Traceview</a> for information about reading trace files. To start logging
+ * trace files, call one of the startMethodTracing() methods. To stop tracing,
+ * call {@link #stopMethodTracing()}.
+ */
+public final class Debug
+{
+    private static final String TAG = "Debug";
+
+    /**
+     * Flags for startMethodTracing().  These can be ORed together.
+     *
+     * TRACE_COUNT_ALLOCS adds the results from startAllocCounting to the
+     * trace key file.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    // This must match VMDebug.TRACE_COUNT_ALLOCS.
+    @Deprecated
+    public static final int TRACE_COUNT_ALLOCS  = 1;
+
+    /**
+     * Flags for printLoadedClasses().  Default behavior is to only show
+     * the class name.
+     */
+    public static final int SHOW_FULL_DETAIL    = 1;
+    public static final int SHOW_CLASSLOADER    = (1 << 1);
+    public static final int SHOW_INITIALIZED    = (1 << 2);
+
+    // set/cleared by waitForDebugger()
+    private static volatile boolean mWaiting = false;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private Debug() {}
+
+    /*
+     * How long to wait for the debugger to finish sending requests.  I've
+     * seen this hit 800msec on the device while waiting for a response
+     * to travel over USB and get processed, so we take that and add
+     * half a second.
+     */
+    private static final int MIN_DEBUGGER_IDLE = 1300;      // msec
+
+    /* how long to sleep when polling for activity */
+    private static final int SPIN_DELAY = 200;              // msec
+
+    /**
+     * Default trace file path and file
+     */
+    private static final String DEFAULT_TRACE_BODY = "dmtrace";
+    private static final String DEFAULT_TRACE_EXTENSION = ".trace";
+
+    /**
+     * This class is used to retrieved various statistics about the memory mappings for this
+     * process. The returned info is broken down by dalvik, native, and other. All results are in kB.
+     */
+    public static class MemoryInfo implements Parcelable {
+        /** The proportional set size for dalvik heap.  (Doesn't include other Dalvik overhead.) */
+        public int dalvikPss;
+        /** The proportional set size that is swappable for dalvik heap. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int dalvikSwappablePss;
+        /** @hide The resident set size for dalvik heap.  (Without other Dalvik overhead.) */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public int dalvikRss;
+        /** The private dirty pages used by dalvik heap. */
+        public int dalvikPrivateDirty;
+        /** The shared dirty pages used by dalvik heap. */
+        public int dalvikSharedDirty;
+        /** The private clean pages used by dalvik heap. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int dalvikPrivateClean;
+        /** The shared clean pages used by dalvik heap. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int dalvikSharedClean;
+        /** The dirty dalvik pages that have been swapped out. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int dalvikSwappedOut;
+        /** The dirty dalvik pages that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public int dalvikSwappedOutPss;
+
+        /** The proportional set size for the native heap. */
+        public int nativePss;
+        /** The proportional set size that is swappable for the native heap. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int nativeSwappablePss;
+        /** @hide The resident set size for the native heap. */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public int nativeRss;
+        /** The private dirty pages used by the native heap. */
+        public int nativePrivateDirty;
+        /** The shared dirty pages used by the native heap. */
+        public int nativeSharedDirty;
+        /** The private clean pages used by the native heap. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int nativePrivateClean;
+        /** The shared clean pages used by the native heap. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int nativeSharedClean;
+        /** The dirty native pages that have been swapped out. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int nativeSwappedOut;
+        /** The dirty native pages that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public int nativeSwappedOutPss;
+
+        /** The proportional set size for everything else. */
+        public int otherPss;
+        /** The proportional set size that is swappable for everything else. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int otherSwappablePss;
+        /** @hide The resident set size for everything else. */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public int otherRss;
+        /** The private dirty pages used by everything else. */
+        public int otherPrivateDirty;
+        /** The shared dirty pages used by everything else. */
+        public int otherSharedDirty;
+        /** The private clean pages used by everything else. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int otherPrivateClean;
+        /** The shared clean pages used by everything else. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int otherSharedClean;
+        /** The dirty pages used by anyting else that have been swapped out. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage
+        public int otherSwappedOut;
+        /** The dirty pages used by anyting else that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public int otherSwappedOutPss;
+
+        /** Whether the kernel reports proportional swap usage */
+        /** @hide */
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public boolean hasSwappedOutPss;
+
+        /** @hide */
+        public static final int HEAP_UNKNOWN = 0;
+        /** @hide */
+        public static final int HEAP_DALVIK = 1;
+        /** @hide */
+        public static final int HEAP_NATIVE = 2;
+
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER = 0;
+        /** @hide */
+        public static final int OTHER_STACK = 1;
+        /** @hide */
+        public static final int OTHER_CURSOR = 2;
+        /** @hide */
+        public static final int OTHER_ASHMEM = 3;
+        /** @hide */
+        public static final int OTHER_GL_DEV = 4;
+        /** @hide */
+        public static final int OTHER_UNKNOWN_DEV = 5;
+        /** @hide */
+        public static final int OTHER_SO = 6;
+        /** @hide */
+        public static final int OTHER_JAR = 7;
+        /** @hide */
+        public static final int OTHER_APK = 8;
+        /** @hide */
+        public static final int OTHER_TTF = 9;
+        /** @hide */
+        public static final int OTHER_DEX = 10;
+        /** @hide */
+        public static final int OTHER_OAT = 11;
+        /** @hide */
+        public static final int OTHER_ART = 12;
+        /** @hide */
+        public static final int OTHER_UNKNOWN_MAP = 13;
+        /** @hide */
+        public static final int OTHER_GRAPHICS = 14;
+        /** @hide */
+        public static final int OTHER_GL = 15;
+        /** @hide */
+        public static final int OTHER_OTHER_MEMTRACK = 16;
+
+        // Needs to be declared here for the DVK_STAT ranges below.
+        /** @hide */
+        @UnsupportedAppUsage
+        public static final int NUM_OTHER_STATS = 17;
+
+        // Dalvik subsections.
+        /** @hide */
+        public static final int OTHER_DALVIK_NORMAL = 17;
+        /** @hide */
+        public static final int OTHER_DALVIK_LARGE = 18;
+        /** @hide */
+        public static final int OTHER_DALVIK_ZYGOTE = 19;
+        /** @hide */
+        public static final int OTHER_DALVIK_NON_MOVING = 20;
+        // Section begins and ends for dumpsys, relative to the DALVIK categories.
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DALVIK_START =
+                OTHER_DALVIK_NORMAL - NUM_OTHER_STATS;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DALVIK_END =
+                OTHER_DALVIK_NON_MOVING - NUM_OTHER_STATS;
+
+        // Dalvik Other subsections.
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_LINEARALLOC = 21;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_ACCOUNTING = 22;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE = 23;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_APP_CODE_CACHE = 24;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_COMPILER_METADATA = 25;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 26;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DALVIK_OTHER_START =
+                OTHER_DALVIK_OTHER_LINEARALLOC - NUM_OTHER_STATS;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DALVIK_OTHER_END =
+                OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE - NUM_OTHER_STATS;
+
+        // Dex subsections (Boot vdex, App dex, and App vdex).
+        /** @hide */
+        public static final int OTHER_DEX_BOOT_VDEX = 27;
+        /** @hide */
+        public static final int OTHER_DEX_APP_DEX = 28;
+        /** @hide */
+        public static final int OTHER_DEX_APP_VDEX = 29;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DEX_START = OTHER_DEX_BOOT_VDEX - NUM_OTHER_STATS;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DEX_END = OTHER_DEX_APP_VDEX - NUM_OTHER_STATS;
+
+        // Art subsections (App image, boot image).
+        /** @hide */
+        public static final int OTHER_ART_APP = 30;
+        /** @hide */
+        public static final int OTHER_ART_BOOT = 31;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_ART_START = OTHER_ART_APP - NUM_OTHER_STATS;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_ART_END = OTHER_ART_BOOT - NUM_OTHER_STATS;
+
+        /** @hide */
+        @UnsupportedAppUsage
+        public static final int NUM_DVK_STATS = OTHER_ART_BOOT + 1 - OTHER_DALVIK_NORMAL;
+
+        /** @hide */
+        public static final int NUM_CATEGORIES = 9;
+
+        /** @hide */
+        public static final int OFFSET_PSS = 0;
+        /** @hide */
+        public static final int OFFSET_SWAPPABLE_PSS = 1;
+        /** @hide */
+        public static final int OFFSET_RSS = 2;
+        /** @hide */
+        public static final int OFFSET_PRIVATE_DIRTY = 3;
+        /** @hide */
+        public static final int OFFSET_SHARED_DIRTY = 4;
+        /** @hide */
+        public static final int OFFSET_PRIVATE_CLEAN = 5;
+        /** @hide */
+        public static final int OFFSET_SHARED_CLEAN = 6;
+        /** @hide */
+        public static final int OFFSET_SWAPPED_OUT = 7;
+        /** @hide */
+        public static final int OFFSET_SWAPPED_OUT_PSS = 8;
+
+        @UnsupportedAppUsage
+        private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
+
+        public MemoryInfo() {
+        }
+
+        /**
+         * @hide Copy contents from another object.
+         */
+        public void set(MemoryInfo other) {
+            dalvikPss = other.dalvikPss;
+            dalvikSwappablePss = other.dalvikSwappablePss;
+            dalvikRss = other.dalvikRss;
+            dalvikPrivateDirty = other.dalvikPrivateDirty;
+            dalvikSharedDirty = other.dalvikSharedDirty;
+            dalvikPrivateClean = other.dalvikPrivateClean;
+            dalvikSharedClean = other.dalvikSharedClean;
+            dalvikSwappedOut = other.dalvikSwappedOut;
+            dalvikSwappedOutPss = other.dalvikSwappedOutPss;
+
+            nativePss = other.nativePss;
+            nativeSwappablePss = other.nativeSwappablePss;
+            nativeRss = other.nativeRss;
+            nativePrivateDirty = other.nativePrivateDirty;
+            nativeSharedDirty = other.nativeSharedDirty;
+            nativePrivateClean = other.nativePrivateClean;
+            nativeSharedClean = other.nativeSharedClean;
+            nativeSwappedOut = other.nativeSwappedOut;
+            nativeSwappedOutPss = other.nativeSwappedOutPss;
+
+            otherPss = other.otherPss;
+            otherSwappablePss = other.otherSwappablePss;
+            otherRss = other.otherRss;
+            otherPrivateDirty = other.otherPrivateDirty;
+            otherSharedDirty = other.otherSharedDirty;
+            otherPrivateClean = other.otherPrivateClean;
+            otherSharedClean = other.otherSharedClean;
+            otherSwappedOut = other.otherSwappedOut;
+            otherSwappedOutPss = other.otherSwappedOutPss;
+
+            hasSwappedOutPss = other.hasSwappedOutPss;
+
+            System.arraycopy(other.otherStats, 0, otherStats, 0, otherStats.length);
+        }
+
+        /**
+         * Return total PSS memory usage in kB.
+         */
+        public int getTotalPss() {
+            return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss();
+        }
+
+        /**
+         * @hide Return total PSS memory usage in kB.
+         */
+        @UnsupportedAppUsage
+        public int getTotalUss() {
+            return dalvikPrivateClean + dalvikPrivateDirty
+                    + nativePrivateClean + nativePrivateDirty
+                    + otherPrivateClean + otherPrivateDirty;
+        }
+
+        /**
+         * Return total PSS memory usage in kB mapping a file of one of the following extension:
+         * .so, .jar, .apk, .ttf, .dex, .odex, .oat, .art .
+         */
+        public int getTotalSwappablePss() {
+            return dalvikSwappablePss + nativeSwappablePss + otherSwappablePss;
+        }
+
+        /**
+         * @hide Return total RSS memory usage in kB.
+         */
+        public int getTotalRss() {
+            return dalvikRss + nativeRss + otherRss;
+        }
+
+        /**
+         * Return total private dirty memory usage in kB.
+         */
+        public int getTotalPrivateDirty() {
+            return dalvikPrivateDirty + nativePrivateDirty + otherPrivateDirty;
+        }
+
+        /**
+         * Return total shared dirty memory usage in kB.
+         */
+        public int getTotalSharedDirty() {
+            return dalvikSharedDirty + nativeSharedDirty + otherSharedDirty;
+        }
+
+        /**
+         * Return total shared clean memory usage in kB.
+         */
+        public int getTotalPrivateClean() {
+            return dalvikPrivateClean + nativePrivateClean + otherPrivateClean;
+        }
+
+        /**
+         * Return total shared clean memory usage in kB.
+         */
+        public int getTotalSharedClean() {
+            return dalvikSharedClean + nativeSharedClean + otherSharedClean;
+        }
+
+        /**
+         * Return total swapped out memory in kB.
+         * @hide
+         */
+        public int getTotalSwappedOut() {
+            return dalvikSwappedOut + nativeSwappedOut + otherSwappedOut;
+        }
+
+        /**
+         * Return total swapped out memory in kB, proportional.
+         * @hide
+         */
+        public int getTotalSwappedOutPss() {
+            return dalvikSwappedOutPss + nativeSwappedOutPss + otherSwappedOutPss;
+        }
+
+        /** @hide */
+        @UnsupportedAppUsage
+        public int getOtherPss(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_PSS];
+        }
+
+        /** @hide */
+        public int getOtherSwappablePss(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPABLE_PSS];
+        }
+
+        /** @hide */
+        public int getOtherRss(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_RSS];
+        }
+
+        /** @hide */
+        @UnsupportedAppUsage
+        public int getOtherPrivateDirty(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_PRIVATE_DIRTY];
+        }
+
+        /** @hide */
+        @UnsupportedAppUsage
+        public int getOtherSharedDirty(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SHARED_DIRTY];
+        }
+
+        /** @hide */
+        public int getOtherPrivateClean(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_PRIVATE_CLEAN];
+        }
+
+        /** @hide */
+        @UnsupportedAppUsage
+        public int getOtherPrivate(int which) {
+          return getOtherPrivateClean(which) + getOtherPrivateDirty(which);
+        }
+
+        /** @hide */
+        public int getOtherSharedClean(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SHARED_CLEAN];
+        }
+
+        /** @hide */
+        public int getOtherSwappedOut(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPED_OUT];
+        }
+
+        /** @hide */
+        public int getOtherSwappedOutPss(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPED_OUT_PSS];
+        }
+
+        /** @hide */
+        @UnsupportedAppUsage
+        public static String getOtherLabel(int which) {
+            switch (which) {
+                case OTHER_DALVIK_OTHER: return "Dalvik Other";
+                case OTHER_STACK: return "Stack";
+                case OTHER_CURSOR: return "Cursor";
+                case OTHER_ASHMEM: return "Ashmem";
+                case OTHER_GL_DEV: return "Gfx dev";
+                case OTHER_UNKNOWN_DEV: return "Other dev";
+                case OTHER_SO: return ".so mmap";
+                case OTHER_JAR: return ".jar mmap";
+                case OTHER_APK: return ".apk mmap";
+                case OTHER_TTF: return ".ttf mmap";
+                case OTHER_DEX: return ".dex mmap";
+                case OTHER_OAT: return ".oat mmap";
+                case OTHER_ART: return ".art mmap";
+                case OTHER_UNKNOWN_MAP: return "Other mmap";
+                case OTHER_GRAPHICS: return "EGL mtrack";
+                case OTHER_GL: return "GL mtrack";
+                case OTHER_OTHER_MEMTRACK: return "Other mtrack";
+                case OTHER_DALVIK_NORMAL: return ".Heap";
+                case OTHER_DALVIK_LARGE: return ".LOS";
+                case OTHER_DALVIK_ZYGOTE: return ".Zygote";
+                case OTHER_DALVIK_NON_MOVING: return ".NonMoving";
+                case OTHER_DALVIK_OTHER_LINEARALLOC: return ".LinearAlloc";
+                case OTHER_DALVIK_OTHER_ACCOUNTING: return ".GC";
+                case OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE: return ".ZygoteJIT";
+                case OTHER_DALVIK_OTHER_APP_CODE_CACHE: return ".AppJIT";
+                case OTHER_DALVIK_OTHER_COMPILER_METADATA: return ".CompilerMetadata";
+                case OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE: return ".IndirectRef";
+                case OTHER_DEX_BOOT_VDEX: return ".Boot vdex";
+                case OTHER_DEX_APP_DEX: return ".App dex";
+                case OTHER_DEX_APP_VDEX: return ".App vdex";
+                case OTHER_ART_APP: return ".App art";
+                case OTHER_ART_BOOT: return ".Boot art";
+                default: return "????";
+            }
+        }
+
+      /**
+       * Returns the value of a particular memory statistic or {@code null} if no
+       * such memory statistic exists.
+       *
+       * <p>The following table lists the memory statistics that are supported.
+       * Note that memory statistics may be added or removed in a future API level.</p>
+       *
+       * <table>
+       *     <thead>
+       *         <tr>
+       *             <th>Memory statistic name</th>
+       *             <th>Meaning</th>
+       *             <th>Example</th>
+       *             <th>Supported (API Levels)</th>
+       *         </tr>
+       *     </thead>
+       *     <tbody>
+       *         <tr>
+       *             <td>summary.java-heap</td>
+       *             <td>The private Java Heap usage in kB. This corresponds to the Java Heap field
+       *                 in the App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.native-heap</td>
+       *             <td>The private Native Heap usage in kB. This corresponds to the Native Heap
+       *                 field in the App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.code</td>
+       *             <td>The memory usage for static code and resources in kB. This corresponds to
+       *                 the Code field in the App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.stack</td>
+       *             <td>The stack usage in kB. This corresponds to the Stack field in the
+       *                 App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.graphics</td>
+       *             <td>The graphics usage in kB. This corresponds to the Graphics field in the
+       *                 App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.private-other</td>
+       *             <td>Other private memory usage in kB. This corresponds to the Private Other
+       *                 field output in the App Summary section by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.system</td>
+       *             <td>Shared and system memory usage in kB. This corresponds to the System
+       *                 field output in the App Summary section by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.total-pss</td>
+       *             <td>Total PSS memory usage in kB.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.total-swap</td>
+       *             <td>Total swap usage in kB.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *     </tbody>
+       * </table>
+       */
+       public String getMemoryStat(String statName) {
+            switch(statName) {
+                case "summary.java-heap":
+                    return Integer.toString(getSummaryJavaHeap());
+                case "summary.native-heap":
+                    return Integer.toString(getSummaryNativeHeap());
+                case "summary.code":
+                    return Integer.toString(getSummaryCode());
+                case "summary.stack":
+                    return Integer.toString(getSummaryStack());
+                case "summary.graphics":
+                    return Integer.toString(getSummaryGraphics());
+                case "summary.private-other":
+                    return Integer.toString(getSummaryPrivateOther());
+                case "summary.system":
+                    return Integer.toString(getSummarySystem());
+                case "summary.total-pss":
+                    return Integer.toString(getSummaryTotalPss());
+                case "summary.total-swap":
+                    return Integer.toString(getSummaryTotalSwap());
+                default:
+                    return null;
+            }
+        }
+
+        /**
+         * Returns a map of the names/values of the memory statistics
+         * that {@link #getMemoryStat(String)} supports.
+         *
+         * @return a map of the names/values of the supported memory statistics.
+         */
+        public Map<String, String> getMemoryStats() {
+            Map<String, String> stats = new HashMap<String, String>();
+            stats.put("summary.java-heap", Integer.toString(getSummaryJavaHeap()));
+            stats.put("summary.native-heap", Integer.toString(getSummaryNativeHeap()));
+            stats.put("summary.code", Integer.toString(getSummaryCode()));
+            stats.put("summary.stack", Integer.toString(getSummaryStack()));
+            stats.put("summary.graphics", Integer.toString(getSummaryGraphics()));
+            stats.put("summary.private-other", Integer.toString(getSummaryPrivateOther()));
+            stats.put("summary.system", Integer.toString(getSummarySystem()));
+            stats.put("summary.total-pss", Integer.toString(getSummaryTotalPss()));
+            stats.put("summary.total-swap", Integer.toString(getSummaryTotalSwap()));
+            return stats;
+        }
+
+        /**
+         * Pss of Java Heap bytes in KB due to the application.
+         * Notes:
+         *  * OTHER_ART is the boot image. Anything private here is blamed on
+         *    the application, not the system.
+         *  * dalvikPrivateDirty includes private zygote, which means the
+         *    application dirtied something allocated by the zygote. We blame
+         *    the application for that memory, not the system.
+         *  * Does not include OTHER_DALVIK_OTHER, which is considered VM
+         *    Overhead and lumped into Private Other.
+         *  * We don't include dalvikPrivateClean, because there should be no
+         *    such thing as private clean for the Java Heap.
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public int getSummaryJavaHeap() {
+            return dalvikPrivateDirty + getOtherPrivate(OTHER_ART);
+        }
+
+        /**
+         * Pss of Native Heap bytes in KB due to the application.
+         * Notes:
+         *  * Includes private dirty malloc space.
+         *  * We don't include nativePrivateClean, because there should be no
+         *    such thing as private clean for the Native Heap.
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public int getSummaryNativeHeap() {
+            return nativePrivateDirty;
+        }
+
+        /**
+         * Pss of code and other static resource bytes in KB due to
+         * the application.
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public int getSummaryCode() {
+            return getOtherPrivate(OTHER_SO)
+              + getOtherPrivate(OTHER_JAR)
+              + getOtherPrivate(OTHER_APK)
+              + getOtherPrivate(OTHER_TTF)
+              + getOtherPrivate(OTHER_DEX)
+                + getOtherPrivate(OTHER_OAT)
+                + getOtherPrivate(OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE)
+                + getOtherPrivate(OTHER_DALVIK_OTHER_APP_CODE_CACHE);
+        }
+
+        /**
+         * Pss in KB of the stack due to the application.
+         * Notes:
+         *  * Includes private dirty stack, which includes both Java and Native
+         *    stack.
+         *  * Does not include private clean stack, because there should be no
+         *    such thing as private clean for the stack.
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public int getSummaryStack() {
+            return getOtherPrivateDirty(OTHER_STACK);
+        }
+
+        /**
+         * Pss in KB of graphics due to the application.
+         * Notes:
+         *  * Includes private Gfx, EGL, and GL.
+         *  * Warning: These numbers can be misreported by the graphics drivers.
+         *  * We don't include shared graphics. It may make sense to, because
+         *    shared graphics are likely buffers due to the application
+         *    anyway, but it's simpler to implement to just group all shared
+         *    memory into the System category.
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public int getSummaryGraphics() {
+            return getOtherPrivate(OTHER_GL_DEV)
+              + getOtherPrivate(OTHER_GRAPHICS)
+              + getOtherPrivate(OTHER_GL);
+        }
+
+        /**
+         * Pss in KB due to the application that haven't otherwise been
+         * accounted for.
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public int getSummaryPrivateOther() {
+            return getTotalPrivateClean()
+              + getTotalPrivateDirty()
+              - getSummaryJavaHeap()
+              - getSummaryNativeHeap()
+              - getSummaryCode()
+              - getSummaryStack()
+              - getSummaryGraphics();
+        }
+
+        /**
+         * Pss in KB due to the system.
+         * Notes:
+         *  * Includes all shared memory.
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public int getSummarySystem() {
+            return getTotalPss()
+              - getTotalPrivateClean()
+              - getTotalPrivateDirty();
+        }
+
+        /**
+         * Rss of Java Heap bytes in KB due to the application.
+         * @hide
+         */
+        public int getSummaryJavaHeapRss() {
+            return dalvikRss + getOtherRss(OTHER_ART);
+        }
+
+        /**
+         * Rss of Native Heap bytes in KB due to the application.
+         * @hide
+         */
+        public int getSummaryNativeHeapRss() {
+            return nativeRss;
+        }
+
+        /**
+         * Rss of code and other static resource bytes in KB due to
+         * the application.
+         * @hide
+         */
+        public int getSummaryCodeRss() {
+            return getOtherRss(OTHER_SO)
+                + getOtherRss(OTHER_JAR)
+                + getOtherRss(OTHER_APK)
+                + getOtherRss(OTHER_TTF)
+                + getOtherRss(OTHER_DEX)
+                + getOtherRss(OTHER_OAT)
+                + getOtherRss(OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE)
+                + getOtherRss(OTHER_DALVIK_OTHER_APP_CODE_CACHE);
+        }
+
+        /**
+         * Rss in KB of the stack due to the application.
+         * @hide
+         */
+        public int getSummaryStackRss() {
+            return getOtherRss(OTHER_STACK);
+        }
+
+        /**
+         * Rss in KB of graphics due to the application.
+         * @hide
+         */
+        public int getSummaryGraphicsRss() {
+            return getOtherRss(OTHER_GL_DEV)
+                + getOtherRss(OTHER_GRAPHICS)
+                + getOtherRss(OTHER_GL);
+        }
+
+        /**
+         * Rss in KB due to either the application or system that haven't otherwise been
+         * accounted for.
+         * @hide
+         */
+        public int getSummaryUnknownRss() {
+            return getTotalRss()
+                - getSummaryJavaHeapRss()
+                - getSummaryNativeHeapRss()
+                - getSummaryCodeRss()
+                - getSummaryStackRss()
+                - getSummaryGraphicsRss();
+        }
+
+        /**
+         * Total Pss in KB.
+         * @hide
+         */
+        public int getSummaryTotalPss() {
+            return getTotalPss();
+        }
+
+        /**
+         * Total Swap in KB.
+         * Notes:
+         *  * Some of this memory belongs in other categories, but we don't
+         *    know if the Swap memory is shared or private, so we don't know
+         *    what to blame on the application and what on the system.
+         *    For now, just lump all the Swap in one place.
+         *    For kernels reporting SwapPss {@link #getSummaryTotalSwapPss()}
+         *    will report the application proportional Swap.
+         * @hide
+         */
+        public int getSummaryTotalSwap() {
+            return getTotalSwappedOut();
+        }
+
+        /**
+         * Total proportional Swap in KB.
+         * Notes:
+         *  * Always 0 if {@link #hasSwappedOutPss} is false.
+         * @hide
+         */
+        public int getSummaryTotalSwapPss() {
+            return getTotalSwappedOutPss();
+        }
+
+        /**
+         * Return true if the kernel is reporting pss swapped out...  that is, if
+         * {@link #getSummaryTotalSwapPss()} will return non-0 values.
+         * @hide
+         */
+        public boolean hasSwappedOutPss() {
+            return hasSwappedOutPss;
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(dalvikPss);
+            dest.writeInt(dalvikSwappablePss);
+            dest.writeInt(dalvikRss);
+            dest.writeInt(dalvikPrivateDirty);
+            dest.writeInt(dalvikSharedDirty);
+            dest.writeInt(dalvikPrivateClean);
+            dest.writeInt(dalvikSharedClean);
+            dest.writeInt(dalvikSwappedOut);
+            dest.writeInt(dalvikSwappedOutPss);
+            dest.writeInt(nativePss);
+            dest.writeInt(nativeSwappablePss);
+            dest.writeInt(nativeRss);
+            dest.writeInt(nativePrivateDirty);
+            dest.writeInt(nativeSharedDirty);
+            dest.writeInt(nativePrivateClean);
+            dest.writeInt(nativeSharedClean);
+            dest.writeInt(nativeSwappedOut);
+            dest.writeInt(nativeSwappedOutPss);
+            dest.writeInt(otherPss);
+            dest.writeInt(otherSwappablePss);
+            dest.writeInt(otherRss);
+            dest.writeInt(otherPrivateDirty);
+            dest.writeInt(otherSharedDirty);
+            dest.writeInt(otherPrivateClean);
+            dest.writeInt(otherSharedClean);
+            dest.writeInt(otherSwappedOut);
+            dest.writeInt(hasSwappedOutPss ? 1 : 0);
+            dest.writeInt(otherSwappedOutPss);
+            dest.writeIntArray(otherStats);
+        }
+
+        public void readFromParcel(Parcel source) {
+            dalvikPss = source.readInt();
+            dalvikSwappablePss = source.readInt();
+            dalvikRss = source.readInt();
+            dalvikPrivateDirty = source.readInt();
+            dalvikSharedDirty = source.readInt();
+            dalvikPrivateClean = source.readInt();
+            dalvikSharedClean = source.readInt();
+            dalvikSwappedOut = source.readInt();
+            dalvikSwappedOutPss = source.readInt();
+            nativePss = source.readInt();
+            nativeSwappablePss = source.readInt();
+            nativeRss = source.readInt();
+            nativePrivateDirty = source.readInt();
+            nativeSharedDirty = source.readInt();
+            nativePrivateClean = source.readInt();
+            nativeSharedClean = source.readInt();
+            nativeSwappedOut = source.readInt();
+            nativeSwappedOutPss = source.readInt();
+            otherPss = source.readInt();
+            otherSwappablePss = source.readInt();
+            otherRss = source.readInt();
+            otherPrivateDirty = source.readInt();
+            otherSharedDirty = source.readInt();
+            otherPrivateClean = source.readInt();
+            otherSharedClean = source.readInt();
+            otherSwappedOut = source.readInt();
+            hasSwappedOutPss = source.readInt() != 0;
+            otherSwappedOutPss = source.readInt();
+            otherStats = source.createIntArray();
+        }
+
+        public static final @android.annotation.NonNull Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() {
+            public MemoryInfo createFromParcel(Parcel source) {
+                return new MemoryInfo(source);
+            }
+            public MemoryInfo[] newArray(int size) {
+                return new MemoryInfo[size];
+            }
+        };
+
+        private MemoryInfo(Parcel source) {
+            readFromParcel(source);
+        }
+    }
+
+
+    /**
+     * Wait until a debugger attaches. As soon as a debugger attaches,
+     * suspend all Java threads and send VM_START (a.k.a VM_INIT)
+     * packet.
+     *
+     * @hide
+     */
+    public static void suspendAllAndSendVmStart() {
+        if (!VMDebug.isDebuggingEnabled()) {
+            return;
+        }
+
+        // if DDMS is listening, inform them of our plight
+        System.out.println("Sending WAIT chunk");
+        byte[] data = new byte[] { 0 };     // 0 == "waiting for debugger"
+        Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1);
+        DdmServer.sendChunk(waitChunk);
+
+        // We must wait until a debugger is connected (debug socket is
+        // open and at least one non-DDM JDWP packedt has been received.
+        // This guarantees that oj-libjdwp has been attached and that
+        // ART's default implementation of suspendAllAndSendVmStart has
+        // been replaced with an implementation that will suspendAll and
+        // send VM_START.
+        System.out.println("Waiting for debugger first packet");
+
+        mWaiting = true;
+        while (!isDebuggerConnected()) {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ie) {
+            }
+        }
+        mWaiting = false;
+
+        System.out.println("Debug.suspendAllAndSentVmStart");
+        VMDebug.suspendAllAndSendVmStart();
+        System.out.println("Debug.suspendAllAndSendVmStart, resumed");
+    }
+
+    /**
+     * Wait until a debugger attaches.  As soon as the debugger attaches,
+     * this returns, so you will need to place a breakpoint after the
+     * waitForDebugger() call if you want to start tracing immediately.
+     */
+    public static void waitForDebugger() {
+        if (!VMDebug.isDebuggingEnabled()) {
+            //System.out.println("debugging not enabled, not waiting");
+            return;
+        }
+        if (isDebuggerConnected())
+            return;
+
+        // if DDMS is listening, inform them of our plight
+        System.out.println("Sending WAIT chunk");
+        byte[] data = new byte[] { 0 };     // 0 == "waiting for debugger"
+        Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1);
+        DdmServer.sendChunk(waitChunk);
+
+        mWaiting = true;
+        while (!isDebuggerConnected()) {
+            try { Thread.sleep(SPIN_DELAY); }
+            catch (InterruptedException ie) {}
+        }
+        mWaiting = false;
+
+        System.out.println("Debugger has connected");
+
+        /*
+         * There is no "ready to go" signal from the debugger, and we're
+         * not allowed to suspend ourselves -- the debugger expects us to
+         * be running happily, and gets confused if we aren't.  We need to
+         * allow the debugger a chance to set breakpoints before we start
+         * running again.
+         *
+         * Sit and spin until the debugger has been idle for a short while.
+         */
+        while (true) {
+            long delta = VMDebug.lastDebuggerActivity();
+            if (delta < 0) {
+                System.out.println("debugger detached?");
+                break;
+            }
+
+            if (delta < MIN_DEBUGGER_IDLE) {
+                System.out.println("waiting for debugger to settle...");
+                try { Thread.sleep(SPIN_DELAY); }
+                catch (InterruptedException ie) {}
+            } else {
+                System.out.println("debugger has settled (" + delta + ")");
+                break;
+            }
+        }
+    }
+
+    /**
+     * Returns "true" if one or more threads is waiting for a debugger
+     * to attach.
+     */
+    public static boolean waitingForDebugger() {
+        return mWaiting;
+    }
+
+    /**
+     * Determine if a debugger is currently attached.
+     */
+    public static boolean isDebuggerConnected() {
+        return VMDebug.isDebuggerConnected();
+    }
+
+    /**
+     * Returns an array of strings that identify VM features.  This is
+     * used by DDMS to determine what sorts of operations the VM can
+     * perform.
+     *
+     * @hide
+     */
+    public static String[] getVmFeatureList() {
+        return VMDebug.getVmFeatureList();
+    }
+
+    /**
+     * Change the JDWP port.
+     *
+     * @deprecated no longer needed or useful
+     */
+    @Deprecated
+    public static void changeDebugPort(int port) {}
+
+    /**
+     * This is the pathname to the sysfs file that enables and disables
+     * tracing on the qemu emulator.
+     */
+    private static final String SYSFS_QEMU_TRACE_STATE = "/sys/qemu_trace/state";
+
+    /**
+     * Enable qemu tracing. For this to work requires running everything inside
+     * the qemu emulator; otherwise, this method will have no effect. The trace
+     * file is specified on the command line when the emulator is started. For
+     * example, the following command line <br />
+     * <code>emulator -trace foo</code><br />
+     * will start running the emulator and create a trace file named "foo". This
+     * method simply enables writing the trace records to the trace file.
+     *
+     * <p>
+     * The main differences between this and {@link #startMethodTracing()} are
+     * that tracing in the qemu emulator traces every cpu instruction of every
+     * process, including kernel code, so we have more complete information,
+     * including all context switches. We can also get more detailed information
+     * such as cache misses. The sequence of calls is determined by
+     * post-processing the instruction trace. The qemu tracing is also done
+     * without modifying the application or perturbing the timing of calls
+     * because no instrumentation is added to the application being traced.
+     * </p>
+     *
+     * <p>
+     * One limitation of using this method compared to using
+     * {@link #startMethodTracing()} on the real device is that the emulator
+     * does not model all of the real hardware effects such as memory and
+     * bus contention.  The emulator also has a simple cache model and cannot
+     * capture all the complexities of a real cache.
+     * </p>
+     */
+    public static void startNativeTracing() {
+        // Open the sysfs file for writing and write "1" to it.
+        PrintWriter outStream = null;
+        try {
+            FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE);
+            outStream = new FastPrintWriter(fos);
+            outStream.println("1");
+        } catch (Exception e) {
+        } finally {
+            if (outStream != null)
+                outStream.close();
+        }
+    }
+
+    /**
+     * Stop qemu tracing.  See {@link #startNativeTracing()} to start tracing.
+     *
+     * <p>Tracing can be started and stopped as many times as desired.  When
+     * the qemu emulator itself is stopped then the buffered trace records
+     * are flushed and written to the trace file.  In fact, it is not necessary
+     * to call this method at all; simply killing qemu is sufficient.  But
+     * starting and stopping a trace is useful for examining a specific
+     * region of code.</p>
+     */
+    public static void stopNativeTracing() {
+        // Open the sysfs file for writing and write "0" to it.
+        PrintWriter outStream = null;
+        try {
+            FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE);
+            outStream = new FastPrintWriter(fos);
+            outStream.println("0");
+        } catch (Exception e) {
+            // We could print an error message here but we probably want
+            // to quietly ignore errors if we are not running in the emulator.
+        } finally {
+            if (outStream != null)
+                outStream.close();
+        }
+    }
+
+    /**
+     * Enable "emulator traces", in which information about the current
+     * method is made available to the "emulator -trace" feature.  There
+     * is no corresponding "disable" call -- this is intended for use by
+     * the framework when tracing should be turned on and left that way, so
+     * that traces captured with F9/F10 will include the necessary data.
+     *
+     * This puts the VM into "profile" mode, which has performance
+     * consequences.
+     *
+     * To temporarily enable tracing, use {@link #startNativeTracing()}.
+     *
+     * @deprecated Please use other tracing method in this class.
+     */
+    public static void enableEmulatorTraceOutput() {
+        Log.w(TAG, "Unimplemented");
+    }
+
+    /**
+     * Start method tracing with default log name and buffer size.
+     * <p>
+     * By default, the trace file is called "dmtrace.trace" and it's placed
+     * under your package-specific directory on primary shared/external storage,
+     * as returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
+     * </p>
+     */
+    public static void startMethodTracing() {
+        VMDebug.startMethodTracing(fixTracePath(null), 0, 0, false, 0);
+    }
+
+    /**
+     * Start method tracing, specifying the trace log file path.
+     * <p>
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
+     * </p>
+     *
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
+     */
+    public static void startMethodTracing(String tracePath) {
+        startMethodTracing(tracePath, 0, 0);
+    }
+
+    /**
+     * Start method tracing, specifying the trace log file name and the buffer
+     * size.
+     * <p>
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
+     * </p>
+     *
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
+     * @param bufferSize The maximum amount of trace data we gather. If not
+     *            given, it defaults to 8MB.
+     */
+    public static void startMethodTracing(String tracePath, int bufferSize) {
+        startMethodTracing(tracePath, bufferSize, 0);
+    }
+
+    /**
+     * Start method tracing, specifying the trace log file name, the buffer
+     * size, and flags.
+     * <p>
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
+     * </p>
+     *
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
+     * @param bufferSize The maximum amount of trace data we gather. If not
+     *            given, it defaults to 8MB.
+     * @param flags Flags to control method tracing. The only one that is
+     *            currently defined is {@link #TRACE_COUNT_ALLOCS}.
+     */
+    public static void startMethodTracing(String tracePath, int bufferSize, int flags) {
+        VMDebug.startMethodTracing(fixTracePath(tracePath), bufferSize, flags, false, 0);
+    }
+
+    /**
+     * Start sampling-based method tracing, specifying the trace log file name,
+     * the buffer size, and the sampling interval.
+     * <p>
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
+     *
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
+     * @param bufferSize The maximum amount of trace data we gather. If not
+     *            given, it defaults to 8MB.
+     * @param intervalUs The amount of time between each sample in microseconds.
+     */
+    public static void startMethodTracingSampling(String tracePath, int bufferSize,
+            int intervalUs) {
+        VMDebug.startMethodTracing(fixTracePath(tracePath), bufferSize, 0, true, intervalUs);
+    }
+
+    /**
+     * Formats name of trace log file for method tracing.
+     */
+    private static String fixTracePath(String tracePath) {
+        if (tracePath == null || tracePath.charAt(0) != '/') {
+            final Context context = AppGlobals.getInitialApplication();
+            final File dir;
+            if (context != null) {
+                dir = context.getExternalFilesDir(null);
+            } else {
+                dir = Environment.getExternalStorageDirectory();
+            }
+
+            if (tracePath == null) {
+                tracePath = new File(dir, DEFAULT_TRACE_BODY).getAbsolutePath();
+            } else {
+                tracePath = new File(dir, tracePath).getAbsolutePath();
+            }
+        }
+        if (!tracePath.endsWith(DEFAULT_TRACE_EXTENSION)) {
+            tracePath += DEFAULT_TRACE_EXTENSION;
+        }
+        return tracePath;
+    }
+
+    /**
+     * Like startMethodTracing(String, int, int), but taking an already-opened
+     * FileDescriptor in which the trace is written.  The file name is also
+     * supplied simply for logging.  Makes a dup of the file descriptor.
+     *
+     * Not exposed in the SDK unless we are really comfortable with supporting
+     * this and find it would be useful.
+     * @hide
+     */
+    public static void startMethodTracing(String traceName, FileDescriptor fd,
+        int bufferSize, int flags, boolean streamOutput) {
+        VMDebug.startMethodTracing(traceName, fd, bufferSize, flags, false, 0, streamOutput);
+    }
+
+    /**
+     * Starts method tracing without a backing file.  When stopMethodTracing
+     * is called, the result is sent directly to DDMS.  (If DDMS is not
+     * attached when tracing ends, the profiling data will be discarded.)
+     *
+     * @hide
+     */
+    public static void startMethodTracingDdms(int bufferSize, int flags,
+        boolean samplingEnabled, int intervalUs) {
+        VMDebug.startMethodTracingDdms(bufferSize, flags, samplingEnabled, intervalUs);
+    }
+
+    /**
+     * Determine whether method tracing is currently active and what type is
+     * active.
+     *
+     * @hide
+     */
+    public static int getMethodTracingMode() {
+        return VMDebug.getMethodTracingMode();
+    }
+
+    /**
+     * Stop method tracing.
+     */
+    public static void stopMethodTracing() {
+        VMDebug.stopMethodTracing();
+    }
+
+    /**
+     * Get an indication of thread CPU usage.  The value returned
+     * indicates the amount of time that the current thread has spent
+     * executing code or waiting for certain types of I/O.
+     *
+     * The time is expressed in nanoseconds, and is only meaningful
+     * when compared to the result from an earlier call.  Note that
+     * nanosecond resolution does not imply nanosecond accuracy.
+     *
+     * On system which don't support this operation, the call returns -1.
+     */
+    public static long threadCpuTimeNanos() {
+        return VMDebug.threadCpuTimeNanos();
+    }
+
+    /**
+     * Start counting the number and aggregate size of memory allocations.
+     *
+     * <p>The {@link #startAllocCounting() start} method resets the counts and enables counting.
+     * The {@link #stopAllocCounting() stop} method disables the counting so that the analysis
+     * code doesn't cause additional allocations.  The various <code>get</code> methods return
+     * the specified value. And the various <code>reset</code> methods reset the specified
+     * count.</p>
+     *
+     * <p>Counts are kept for the system as a whole (global) and for each thread.
+     * The per-thread counts for threads other than the current thread
+     * are not cleared by the "reset" or "start" calls.</p>
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void startAllocCounting() {
+        VMDebug.startAllocCounting();
+    }
+
+    /**
+     * Stop counting the number and aggregate size of memory allocations.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void stopAllocCounting() {
+        VMDebug.stopAllocCounting();
+    }
+
+    /**
+     * Returns the global count of objects allocated by the runtime between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getGlobalAllocCount() {
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS);
+    }
+
+    /**
+     * Clears the global count of objects allocated.
+     * @see #getGlobalAllocCount()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetGlobalAllocCount() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS);
+    }
+
+    /**
+     * Returns the global size, in bytes, of objects allocated by the runtime between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getGlobalAllocSize() {
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES);
+    }
+
+    /**
+     * Clears the global size of objects allocated.
+     * @see #getGlobalAllocSize()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetGlobalAllocSize() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES);
+    }
+
+    /**
+     * Returns the global count of objects freed by the runtime between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getGlobalFreedCount() {
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS);
+    }
+
+    /**
+     * Clears the global count of objects freed.
+     * @see #getGlobalFreedCount()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetGlobalFreedCount() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS);
+    }
+
+    /**
+     * Returns the global size, in bytes, of objects freed by the runtime between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getGlobalFreedSize() {
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES);
+    }
+
+    /**
+     * Clears the global size of objects freed.
+     * @see #getGlobalFreedSize()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetGlobalFreedSize() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES);
+    }
+
+    /**
+     * Returns the number of non-concurrent GC invocations between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getGlobalGcInvocationCount() {
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS);
+    }
+
+    /**
+     * Clears the count of non-concurrent GC invocations.
+     * @see #getGlobalGcInvocationCount()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetGlobalGcInvocationCount() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS);
+    }
+
+    /**
+     * Returns the number of classes successfully initialized (ie those that executed without
+     * throwing an exception) between a {@link #startAllocCounting() start} and
+     * {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getGlobalClassInitCount() {
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT);
+    }
+
+    /**
+     * Clears the count of classes initialized.
+     * @see #getGlobalClassInitCount()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetGlobalClassInitCount() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT);
+    }
+
+    /**
+     * Returns the time spent successfully initializing classes between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getGlobalClassInitTime() {
+        /* cumulative elapsed time for class initialization, in usec */
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME);
+    }
+
+    /**
+     * Clears the count of time spent initializing classes.
+     * @see #getGlobalClassInitTime()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetGlobalClassInitTime() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME);
+    }
+
+    /**
+     * This method exists for compatibility and always returns 0.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int getGlobalExternalAllocCount() {
+        return 0;
+    }
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void resetGlobalExternalAllocSize() {}
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void resetGlobalExternalAllocCount() {}
+
+    /**
+     * This method exists for compatibility and always returns 0.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int getGlobalExternalAllocSize() {
+        return 0;
+    }
+
+    /**
+     * This method exists for compatibility and always returns 0.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int getGlobalExternalFreedCount() {
+        return 0;
+    }
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void resetGlobalExternalFreedCount() {}
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int getGlobalExternalFreedSize() {
+        return 0;
+    }
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void resetGlobalExternalFreedSize() {}
+
+    /**
+     * Returns the thread-local count of objects allocated by the runtime between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getThreadAllocCount() {
+        return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS);
+    }
+
+    /**
+     * Clears the thread-local count of objects allocated.
+     * @see #getThreadAllocCount()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetThreadAllocCount() {
+        VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS);
+    }
+
+    /**
+     * Returns the thread-local size of objects allocated by the runtime between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     * @return The allocated size in bytes.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getThreadAllocSize() {
+        return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES);
+    }
+
+    /**
+     * Clears the thread-local count of objects allocated.
+     * @see #getThreadAllocSize()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetThreadAllocSize() {
+        VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES);
+    }
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int getThreadExternalAllocCount() {
+        return 0;
+    }
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void resetThreadExternalAllocCount() {}
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int getThreadExternalAllocSize() {
+        return 0;
+    }
+
+    /**
+     * This method exists for compatibility and has no effect.
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static void resetThreadExternalAllocSize() {}
+
+    /**
+     * Returns the number of thread-local non-concurrent GC invocations between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static int getThreadGcInvocationCount() {
+        return VMDebug.getAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS);
+    }
+
+    /**
+     * Clears the thread-local count of non-concurrent GC invocations.
+     * @see #getThreadGcInvocationCount()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetThreadGcInvocationCount() {
+        VMDebug.resetAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS);
+    }
+
+    /**
+     * Clears all the global and thread-local memory allocation counters.
+     * @see #startAllocCounting()
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public static void resetAllCounts() {
+        VMDebug.resetAllocCount(VMDebug.KIND_ALL_COUNTS);
+    }
+
+    /**
+     * Returns the value of a particular runtime statistic or {@code null} if no
+     * such runtime statistic exists.
+     *
+     * <p>The following table lists the runtime statistics that the runtime supports.
+     * All statistics are approximate. Individual allocations may not be immediately reflected
+     * in the results.
+     * Note runtime statistics may be added or removed in a future API level.</p>
+     *
+     * <table>
+     *     <thead>
+     *         <tr>
+     *             <th>Runtime statistic name</th>
+     *             <th>Meaning</th>
+     *             <th>Example</th>
+     *             <th>Supported (API Levels)</th>
+     *         </tr>
+     *     </thead>
+     *     <tbody>
+     *         <tr>
+     *             <td>art.gc.gc-count</td>
+     *             <td>The number of garbage collection runs.</td>
+     *             <td>{@code 164}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.gc-time</td>
+     *             <td>The total duration of garbage collection runs in ms.</td>
+     *             <td>{@code 62364}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.bytes-allocated</td>
+     *             <td>The total number of bytes that the application allocated.</td>
+     *             <td>{@code 1463948408}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.bytes-freed</td>
+     *             <td>The total number of bytes that garbage collection reclaimed.</td>
+     *             <td>{@code 1313493084}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.blocking-gc-count</td>
+     *             <td>The number of blocking garbage collection runs.</td>
+     *             <td>{@code 2}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.blocking-gc-time</td>
+     *             <td>The total duration of blocking garbage collection runs in ms.</td>
+     *             <td>{@code 804}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.gc-count-rate-histogram</td>
+     *             <td>Every 10 seconds, the gc-count-rate is computed as the number of garbage
+     *                 collection runs that have occurred over the last 10
+     *                 seconds. art.gc.gc-count-rate-histogram is a histogram of the gc-count-rate
+     *                 samples taken since the process began. The histogram can be used to identify
+     *                 instances of high rates of garbage collection runs. For example, a histogram
+     *                 of "0:34503,1:45350,2:11281,3:8088,4:43,5:8" shows that most of the time
+     *                 there are between 0 and 2 garbage collection runs every 10 seconds, but there
+     *                 were 8 distinct 10-second intervals in which 5 garbage collection runs
+     *                 occurred.</td>
+     *             <td>{@code 0:34503,1:45350,2:11281,3:8088,4:43,5:8}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.blocking-gc-count-rate-histogram</td>
+     *             <td>Every 10 seconds, the blocking-gc-count-rate is computed as the number of
+     *                 blocking garbage collection runs that have occurred over the last 10
+     *                 seconds. art.gc.blocking-gc-count-rate-histogram is a histogram of the
+     *                 blocking-gc-count-rate samples taken since the process began. The histogram
+     *                 can be used to identify instances of high rates of blocking garbage
+     *                 collection runs. For example, a histogram of "0:99269,1:1,2:1" shows that
+     *                 most of the time there are zero blocking garbage collection runs every 10
+     *                 seconds, but there was one 10-second interval in which one blocking garbage
+     *                 collection run occurred, and there was one interval in which two blocking
+     *                 garbage collection runs occurred.</td>
+     *             <td>{@code 0:99269,1:1,2:1}</td>
+     *             <td>23</td>
+     *         </tr>
+     *     </tbody>
+     * </table>
+     *
+     * @param statName
+     *            the name of the runtime statistic to look up.
+     * @return the value of the specified runtime statistic or {@code null} if the
+     *         runtime statistic doesn't exist.
+     */
+    public static String getRuntimeStat(String statName) {
+        return VMDebug.getRuntimeStat(statName);
+    }
+
+    /**
+     * Returns a map of the names/values of the runtime statistics
+     * that {@link #getRuntimeStat(String)} supports.
+     *
+     * @return a map of the names/values of the supported runtime statistics.
+     */
+    public static Map<String, String> getRuntimeStats() {
+        return VMDebug.getRuntimeStats();
+    }
+
+    /**
+     * Returns the size of the native heap.
+     * @return The size of the native heap in bytes.
+     */
+    public static native long getNativeHeapSize();
+
+    /**
+     * Returns the amount of allocated memory in the native heap.
+     * @return The allocated size in bytes.
+     */
+    public static native long getNativeHeapAllocatedSize();
+
+    /**
+     * Returns the amount of free memory in the native heap.
+     * @return The freed size in bytes.
+     */
+    public static native long getNativeHeapFreeSize();
+
+    /**
+     * Retrieves information about this processes memory usages. This information is broken down by
+     * how much is in use by dalvik, the native heap, and everything else.
+     *
+     * <p><b>Note:</b> this method directly retrieves memory information for the given process
+     * from low-level data available to it.  It may not be able to retrieve information about
+     * some protected allocations, such as graphics.  If you want to be sure you can see
+     * all information about allocations by the process, use
+     * {@link android.app.ActivityManager#getProcessMemoryInfo(int[])} instead.</p>
+     */
+    public static native void getMemoryInfo(MemoryInfo memoryInfo);
+
+    /**
+     * Note: currently only works when the requested pid has the same UID
+     * as the caller.
+     *
+     * @return true if the meminfo was read successfully, false if not (i.e., given pid has gone).
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static native boolean getMemoryInfo(int pid, MemoryInfo memoryInfo);
+
+    /**
+     * Retrieves the PSS memory used by the process as given by the
+     * smaps.
+     */
+    public static native long getPss();
+
+    /**
+     * Retrieves the PSS memory used by the process as given by the smaps. Optionally supply a long
+     * array of up to 3 entries to also receive (up to 3 values in order): the Uss and SwapPss and
+     * Rss (only filled in as of {@link android.os.Build.VERSION_CODES#P}) of the process, and
+     * another array to also retrieve the separate memtrack sizes (up to 4 values in order): the
+     * total memtrack reported size, memtrack graphics, memtrack gl and memtrack other.
+     *
+     * @return The PSS memory usage, or 0 if failed to retrieve (i.e., given pid has gone).
+     * @hide
+     */
+    public static native long getPss(int pid, long[] outUssSwapPssRss, long[] outMemtrack);
+
+    /** @hide */
+    public static final int MEMINFO_TOTAL = 0;
+    /** @hide */
+    public static final int MEMINFO_FREE = 1;
+    /** @hide */
+    public static final int MEMINFO_BUFFERS = 2;
+    /** @hide */
+    public static final int MEMINFO_CACHED = 3;
+    /** @hide */
+    public static final int MEMINFO_SHMEM = 4;
+    /** @hide */
+    public static final int MEMINFO_SLAB = 5;
+     /** @hide */
+    public static final int MEMINFO_SLAB_RECLAIMABLE = 6;
+     /** @hide */
+    public static final int MEMINFO_SLAB_UNRECLAIMABLE = 7;
+    /** @hide */
+    public static final int MEMINFO_SWAP_TOTAL = 8;
+    /** @hide */
+    public static final int MEMINFO_SWAP_FREE = 9;
+    /** @hide */
+    public static final int MEMINFO_ZRAM_TOTAL = 10;
+    /** @hide */
+    public static final int MEMINFO_MAPPED = 11;
+    /** @hide */
+    public static final int MEMINFO_VM_ALLOC_USED = 12;
+    /** @hide */
+    public static final int MEMINFO_PAGE_TABLES = 13;
+    /** @hide */
+    public static final int MEMINFO_KERNEL_STACK = 14;
+    /**
+     * Note: MEMINFO_KRECLAIMABLE includes MEMINFO_SLAB_RECLAIMABLE (see KReclaimable field
+     * description in kernel documentation).
+     * @hide
+     */
+    public static final int MEMINFO_KRECLAIMABLE = 15;
+    /** @hide */
+    public static final int MEMINFO_ACTIVE = 16;
+    /** @hide */
+    public static final int MEMINFO_INACTIVE = 17;
+    /** @hide */
+    public static final int MEMINFO_UNEVICTABLE = 18;
+    /** @hide */
+    public static final int MEMINFO_AVAILABLE = 19;
+    /** @hide */
+    public static final int MEMINFO_ACTIVE_ANON = 20;
+    /** @hide */
+    public static final int MEMINFO_INACTIVE_ANON = 21;
+    /** @hide */
+    public static final int MEMINFO_ACTIVE_FILE = 22;
+    /** @hide */
+    public static final int MEMINFO_INACTIVE_FILE = 23;
+    /** @hide */
+    public static final int MEMINFO_CMA_TOTAL = 24;
+    /** @hide */
+    public static final int MEMINFO_CMA_FREE = 25;
+    /** @hide */
+    public static final int MEMINFO_COUNT = 26;
+
+    /**
+     * Retrieves /proc/meminfo.  outSizes is filled with fields
+     * as defined by MEMINFO_* offsets.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static native void getMemInfo(long[] outSizes);
+
+    /**
+     * Establish an object allocation limit in the current thread.
+     * This feature was never enabled in release builds.  The
+     * allocation limits feature was removed in Honeycomb.  This
+     * method exists for compatibility and always returns -1 and has
+     * no effect.
+     *
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int setAllocationLimit(int limit) {
+        return -1;
+    }
+
+    /**
+     * Establish a global object allocation limit.  This feature was
+     * never enabled in release builds.  The allocation limits feature
+     * was removed in Honeycomb.  This method exists for compatibility
+     * and always returns -1 and has no effect.
+     *
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public static int setGlobalAllocationLimit(int limit) {
+        return -1;
+    }
+
+    /**
+     * Dump a list of all currently loaded class to the log file.
+     *
+     * @param flags See constants above.
+     */
+    public static void printLoadedClasses(int flags) {
+        VMDebug.printLoadedClasses(flags);
+    }
+
+    /**
+     * Get the number of loaded classes.
+     * @return the number of loaded classes.
+     */
+    public static int getLoadedClassCount() {
+        return VMDebug.getLoadedClassCount();
+    }
+
+    /**
+     * Dump "hprof" data to the specified file.  This may cause a GC.
+     *
+     * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof").
+     * @throws UnsupportedOperationException if the VM was built without
+     *         HPROF support.
+     * @throws IOException if an error occurs while opening or writing files.
+     */
+    public static void dumpHprofData(String fileName) throws IOException {
+        VMDebug.dumpHprofData(fileName);
+    }
+
+    /**
+     * Like dumpHprofData(String), but takes an already-opened
+     * FileDescriptor to which the trace is written.  The file name is also
+     * supplied simply for logging.  Makes a dup of the file descriptor.
+     *
+     * Primarily for use by the "am" shell command.
+     *
+     * @hide
+     */
+    public static void dumpHprofData(String fileName, FileDescriptor fd)
+            throws IOException {
+        VMDebug.dumpHprofData(fileName, fd);
+    }
+
+    /**
+     * Collect "hprof" and send it to DDMS.  This may cause a GC.
+     *
+     * @throws UnsupportedOperationException if the VM was built without
+     *         HPROF support.
+     * @hide
+     */
+    public static void dumpHprofDataDdms() {
+        VMDebug.dumpHprofDataDdms();
+    }
+
+    /**
+     * Writes native heap data to the specified file descriptor.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static native void dumpNativeHeap(FileDescriptor fd);
+
+    /**
+     * Writes malloc info data to the specified file descriptor.
+     *
+     * @hide
+     */
+    public static native void dumpNativeMallocInfo(FileDescriptor fd);
+
+    /**
+      * Returns a count of the extant instances of a class.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static long countInstancesOfClass(Class cls) {
+        return VMDebug.countInstancesOfClass(cls, true);
+    }
+
+    /**
+     * Returns the number of sent transactions from this process.
+     * @return The number of sent transactions or -1 if it could not read t.
+     */
+    public static native int getBinderSentTransactions();
+
+    /**
+     * Returns the number of received transactions from the binder driver.
+     * @return The number of received transactions or -1 if it could not read the stats.
+     */
+    public static native int getBinderReceivedTransactions();
+
+    /**
+     * Returns the number of active local Binder objects that exist in the
+     * current process.
+     */
+    public static final native int getBinderLocalObjectCount();
+
+    /**
+     * Returns the number of references to remote proxy Binder objects that
+     * exist in the current process.
+     */
+    public static final native int getBinderProxyObjectCount();
+
+    /**
+     * Returns the number of death notification links to Binder objects that
+     * exist in the current process.
+     */
+    public static final native int getBinderDeathObjectCount();
+
+    /**
+     * Dumps the contents of VM reference tables (e.g. JNI locals and
+     * globals) to the log file.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final void dumpReferenceTables() {
+        VMDebug.dumpReferenceTables();
+    }
+
+    /**
+     * API for gathering and querying instruction counts.
+     *
+     * Example usage:
+     * <pre>
+     *   Debug.InstructionCount icount = new Debug.InstructionCount();
+     *   icount.resetAndStart();
+     *    [... do lots of stuff ...]
+     *   if (icount.collect()) {
+     *       System.out.println("Total instructions executed: "
+     *           + icount.globalTotal());
+     *       System.out.println("Method invocations: "
+     *           + icount.globalMethodInvocations());
+     *   }
+     * </pre>
+     *
+     * @deprecated Instruction counting is no longer supported.
+     */
+    @Deprecated
+    public static class InstructionCount {
+        public InstructionCount() {
+        }
+
+        /**
+         * Reset counters and ensure counts are running.  Counts may
+         * have already been running.
+         *
+         * @return true if counting was started
+         */
+        public boolean resetAndStart() {
+            return false;
+        }
+
+        /**
+         * Collect instruction counts.  May or may not stop the
+         * counting process.
+         */
+        public boolean collect() {
+            return false;
+        }
+
+        /**
+         * Return the total number of instructions executed globally (i.e. in
+         * all threads).
+         */
+        public int globalTotal() {
+            return 0;
+        }
+
+        /**
+         * Return the total number of method-invocation instructions
+         * executed globally.
+         */
+        public int globalMethodInvocations() {
+            return 0;
+        }
+    }
+
+    /**
+     * A Map of typed debug properties.
+     */
+    private static final TypedProperties debugProperties;
+
+    /*
+     * Load the debug properties from the standard files into debugProperties.
+     */
+    static {
+        if (false) {
+            final String TAG = "DebugProperties";
+            final String[] files = { "/system/debug.prop", "/debug.prop", "/data/debug.prop" };
+            final TypedProperties tp = new TypedProperties();
+
+            // Read the properties from each of the files, if present.
+            for (String file : files) {
+                Reader r;
+                try {
+                    r = new FileReader(file);
+                } catch (FileNotFoundException ex) {
+                    // It's ok if a file is missing.
+                    continue;
+                }
+
+                try {
+                    tp.load(r);
+                } catch (Exception ex) {
+                    throw new RuntimeException("Problem loading " + file, ex);
+                } finally {
+                    try {
+                        r.close();
+                    } catch (IOException ex) {
+                        // Ignore this error.
+                    }
+                }
+            }
+
+            debugProperties = tp.isEmpty() ? null : tp;
+        } else {
+            debugProperties = null;
+        }
+    }
+
+
+    /**
+     * Returns true if the type of the field matches the specified class.
+     * Handles the case where the class is, e.g., java.lang.Boolean, but
+     * the field is of the primitive "boolean" type.  Also handles all of
+     * the java.lang.Number subclasses.
+     */
+    private static boolean fieldTypeMatches(Field field, Class<?> cl) {
+        Class<?> fieldClass = field.getType();
+        if (fieldClass == cl) {
+            return true;
+        }
+        Field primitiveTypeField;
+        try {
+            /* All of the classes we care about (Boolean, Integer, etc.)
+             * have a Class field called "TYPE" that points to the corresponding
+             * primitive class.
+             */
+            primitiveTypeField = cl.getField("TYPE");
+        } catch (NoSuchFieldException ex) {
+            return false;
+        }
+        try {
+            return fieldClass == (Class<?>) primitiveTypeField.get(null);
+        } catch (IllegalAccessException ex) {
+            return false;
+        }
+    }
+
+
+    /**
+     * Looks up the property that corresponds to the field, and sets the field's value
+     * if the types match.
+     */
+    private static void modifyFieldIfSet(final Field field, final TypedProperties properties,
+                                         final String propertyName) {
+        if (field.getType() == java.lang.String.class) {
+            int stringInfo = properties.getStringInfo(propertyName);
+            switch (stringInfo) {
+                case TypedProperties.STRING_SET:
+                    // Handle as usual below.
+                    break;
+                case TypedProperties.STRING_NULL:
+                    try {
+                        field.set(null, null);  // null object for static fields; null string
+                    } catch (IllegalAccessException ex) {
+                        throw new IllegalArgumentException(
+                            "Cannot set field for " + propertyName, ex);
+                    }
+                    return;
+                case TypedProperties.STRING_NOT_SET:
+                    return;
+                case TypedProperties.STRING_TYPE_MISMATCH:
+                    throw new IllegalArgumentException(
+                        "Type of " + propertyName + " " +
+                        " does not match field type (" + field.getType() + ")");
+                default:
+                    throw new IllegalStateException(
+                        "Unexpected getStringInfo(" + propertyName + ") return value " +
+                        stringInfo);
+            }
+        }
+        Object value = properties.get(propertyName);
+        if (value != null) {
+            if (!fieldTypeMatches(field, value.getClass())) {
+                throw new IllegalArgumentException(
+                    "Type of " + propertyName + " (" + value.getClass() + ") " +
+                    " does not match field type (" + field.getType() + ")");
+            }
+            try {
+                field.set(null, value);  // null object for static fields
+            } catch (IllegalAccessException ex) {
+                throw new IllegalArgumentException(
+                    "Cannot set field for " + propertyName, ex);
+            }
+        }
+    }
+
+
+    /**
+     * Equivalent to <code>setFieldsOn(cl, false)</code>.
+     *
+     * @see #setFieldsOn(Class, boolean)
+     *
+     * @hide
+     */
+    public static void setFieldsOn(Class<?> cl) {
+        setFieldsOn(cl, false);
+    }
+
+    /**
+     * Reflectively sets static fields of a class based on internal debugging
+     * properties.  This method is a no-op if false is
+     * false.
+     * <p>
+     * <strong>NOTE TO APPLICATION DEVELOPERS</strong>: false will
+     * always be false in release builds.  This API is typically only useful
+     * for platform developers.
+     * </p>
+     * Class setup: define a class whose only fields are non-final, static
+     * primitive types (except for "char") or Strings.  In a static block
+     * after the field definitions/initializations, pass the class to
+     * this method, Debug.setFieldsOn(). Example:
+     * <pre>
+     * package com.example;
+     *
+     * import android.os.Debug;
+     *
+     * public class MyDebugVars {
+     *    public static String s = "a string";
+     *    public static String s2 = "second string";
+     *    public static String ns = null;
+     *    public static boolean b = false;
+     *    public static int i = 5;
+     *    @Debug.DebugProperty
+     *    public static float f = 0.1f;
+     *    @@Debug.DebugProperty
+     *    public static double d = 0.5d;
+     *
+     *    // This MUST appear AFTER all fields are defined and initialized!
+     *    static {
+     *        // Sets all the fields
+     *        Debug.setFieldsOn(MyDebugVars.class);
+     *
+     *        // Sets only the fields annotated with @Debug.DebugProperty
+     *        // Debug.setFieldsOn(MyDebugVars.class, true);
+     *    }
+     * }
+     * </pre>
+     * setFieldsOn() may override the value of any field in the class based
+     * on internal properties that are fixed at boot time.
+     * <p>
+     * These properties are only set during platform debugging, and are not
+     * meant to be used as a general-purpose properties store.
+     *
+     * {@hide}
+     *
+     * @param cl The class to (possibly) modify
+     * @param partial If false, sets all static fields, otherwise, only set
+     *        fields with the {@link android.os.Debug.DebugProperty}
+     *        annotation
+     * @throws IllegalArgumentException if any fields are final or non-static,
+     *         or if the type of the field does not match the type of
+     *         the internal debugging property value.
+     */
+    public static void setFieldsOn(Class<?> cl, boolean partial) {
+        if (false) {
+            if (debugProperties != null) {
+                /* Only look for fields declared directly by the class,
+                 * so we don't mysteriously change static fields in superclasses.
+                 */
+                for (Field field : cl.getDeclaredFields()) {
+                    if (!partial || field.getAnnotation(DebugProperty.class) != null) {
+                        final String propertyName = cl.getName() + "." + field.getName();
+                        boolean isStatic = Modifier.isStatic(field.getModifiers());
+                        boolean isFinal = Modifier.isFinal(field.getModifiers());
+
+                        if (!isStatic || isFinal) {
+                            throw new IllegalArgumentException(propertyName +
+                                " must be static and non-final");
+                        }
+                        modifyFieldIfSet(field, debugProperties, propertyName);
+                    }
+                }
+            }
+        } else {
+            Log.wtf(TAG,
+                  "setFieldsOn(" + (cl == null ? "null" : cl.getName()) +
+                  ") called in non-DEBUG build");
+        }
+    }
+
+    /**
+     * Annotation to put on fields you want to set with
+     * {@link Debug#setFieldsOn(Class, boolean)}.
+     *
+     * @hide
+     */
+    @Target({ ElementType.FIELD })
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface DebugProperty {
+    }
+
+    /**
+     * Get a debugging dump of a system service by name.
+     *
+     * <p>Most services require the caller to hold android.permission.DUMP.
+     *
+     * @param name of the service to dump
+     * @param fd to write dump output to (usually an output log file)
+     * @param args to pass to the service's dump method, may be null
+     * @return true if the service was dumped successfully, false if
+     *     the service could not be found or had an error while dumping
+     */
+    public static boolean dumpService(String name, FileDescriptor fd, String[] args) {
+        IBinder service = ServiceManager.getService(name);
+        if (service == null) {
+            Log.e(TAG, "Can't find service to dump: " + name);
+            return false;
+        }
+
+        try {
+            service.dump(fd, args);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Can't dump service: " + name, e);
+            return false;
+        }
+    }
+
+    /**
+     * Append the Java stack traces of a given native process to a specified file.
+     *
+     * @param pid pid to dump.
+     * @param file path of file to append dump to.
+     * @param timeoutSecs time to wait in seconds, or 0 to wait forever.
+     * @hide
+     */
+    public static native boolean dumpJavaBacktraceToFileTimeout(int pid, String file,
+                                                                int timeoutSecs);
+
+    /**
+     * Append the native stack traces of a given process to a specified file.
+     *
+     * @param pid pid to dump.
+     * @param file path of file to append dump to.
+     * @param timeoutSecs time to wait in seconds, or 0 to wait forever.
+     * @hide
+     */
+    public static native boolean dumpNativeBacktraceToFileTimeout(int pid, String file,
+                                                                  int timeoutSecs);
+
+    /**
+     * Get description of unreachable native memory.
+     * @param limit the number of leaks to provide info on, 0 to only get a summary.
+     * @param contents true to include a hex dump of the contents of unreachable memory.
+     * @return the String containing a description of unreachable memory.
+     * @hide */
+    public static native String getUnreachableMemory(int limit, boolean contents);
+
+    /**
+     * Return a String describing the calling method and location at a particular stack depth.
+     * @param callStack the Thread stack
+     * @param depth the depth of stack to return information for.
+     * @return the String describing the caller at that depth.
+     */
+    private static String getCaller(StackTraceElement callStack[], int depth) {
+        // callStack[4] is the caller of the method that called getCallers()
+        if (4 + depth >= callStack.length) {
+            return "<bottom of call stack>";
+        }
+        StackTraceElement caller = callStack[4 + depth];
+        return caller.getClassName() + "." + caller.getMethodName() + ":" + caller.getLineNumber();
+    }
+
+    /**
+     * Return a string consisting of methods and locations at multiple call stack levels.
+     * @param depth the number of levels to return, starting with the immediate caller.
+     * @return a string describing the call stack.
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public static String getCallers(final int depth) {
+        final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < depth; i++) {
+            sb.append(getCaller(callStack, i)).append(" ");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Return a string consisting of methods and locations at multiple call stack levels.
+     * @param depth the number of levels to return, starting with the immediate caller.
+     * @return a string describing the call stack.
+     * {@hide}
+     */
+    public static String getCallers(final int start, int depth) {
+        final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+        StringBuilder sb = new StringBuilder();
+        depth += start;
+        for (int i = start; i < depth; i++) {
+            sb.append(getCaller(callStack, i)).append(" ");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Like {@link #getCallers(int)}, but each location is append to the string
+     * as a new line with <var>linePrefix</var> in front of it.
+     * @param depth the number of levels to return, starting with the immediate caller.
+     * @param linePrefix prefix to put in front of each location.
+     * @return a string describing the call stack.
+     * {@hide}
+     */
+    public static String getCallers(final int depth, String linePrefix) {
+        final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < depth; i++) {
+            sb.append(linePrefix).append(getCaller(callStack, i)).append("\n");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * @return a String describing the immediate caller of the calling method.
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public static String getCaller() {
+        return getCaller(Thread.currentThread().getStackTrace(), 0);
+    }
+
+    /**
+     * Attach a library as a jvmti agent to the current runtime, with the given classloader
+     * determining the library search path.
+     * <p>
+     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
+     * throw a SecurityException.
+     *
+     * @param library the library containing the agent.
+     * @param options the options passed to the agent.
+     * @param classLoader the classloader determining the library search path.
+     *
+     * @throws IOException if the agent could not be attached.
+     * @throws SecurityException if the app is not debuggable.
+     */
+    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
+            @Nullable ClassLoader classLoader) throws IOException {
+        Preconditions.checkNotNull(library);
+        Preconditions.checkArgument(!library.contains("="));
+
+        if (options == null) {
+            VMDebug.attachAgent(library, classLoader);
+        } else {
+            VMDebug.attachAgent(library + "=" + options, classLoader);
+        }
+    }
+
+    /**
+     * Return the current free ZRAM usage in kilobytes.
+     *
+     * @hide
+     */
+    public static native long getZramFreeKb();
+
+    /**
+     * Return total memory size in kilobytes for exported DMA-BUFs or -1 if
+     * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read.
+     *
+     * @hide
+     */
+    public static native long getDmabufTotalExportedKb();
+
+    /**
+     * Return total memory size in kilobytes for DMA-BUFs exported from the DMA-BUF
+     * heaps frameworks or -1 in the case of an error.
+     *
+     * @hide
+     */
+    public static native long getDmabufHeapTotalExportedKb();
+
+    /**
+     * Return memory size in kilobytes allocated for ION heaps or -1 if
+     * /sys/kernel/ion/total_heaps_kb could not be read.
+     *
+     * @hide
+     */
+    public static native long getIonHeapsSizeKb();
+
+    /**
+     * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if
+     * /sys/kernel/dma_heap/total_pools_kb could not be read.
+     *
+     * @hide
+     */
+    public static native long getDmabufHeapPoolsSizeKb();
+
+    /**
+     * Return memory size in kilobytes allocated for ION pools or -1 if
+     * /sys/kernel/ion/total_pools_kb could not be read.
+     *
+     * @hide
+     */
+    public static native long getIonPoolsSizeKb();
+
+    /**
+     * Returns the global total GPU-private memory in kB or -1 on error.
+     *
+     * @hide
+     */
+    public static native long getGpuPrivateMemoryKb();
+
+    /**
+     * Return DMA-BUF memory mapped by processes in kB.
+     * Notes:
+     *  * Warning: Might impact performance as it reads /proc/<pid>/maps files for each process.
+     *
+     * @hide
+     */
+    public static native long getDmabufMappedSizeKb();
+
+    /**
+     * Return memory size in kilobytes used by GPU.
+     *
+     * @hide
+     */
+    public static native long getGpuTotalUsageKb();
+
+    /**
+     * Return whether virtually-mapped kernel stacks are enabled (CONFIG_VMAP_STACK).
+     * Note: caller needs config_gz read sepolicy permission
+     *
+     * @hide
+     */
+    public static native boolean isVmapStack();
+}
diff --git a/android-34/android/os/DeviceIdleManager.java b/android-34/android/os/DeviceIdleManager.java
new file mode 100644
index 0000000..6cdf585
--- /dev/null
+++ b/android-34/android/os/DeviceIdleManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+
+/**
+ * Access to the service that keeps track of device idleness and drives low power mode based on
+ * that.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.DEVICE_IDLE_CONTROLLER)
+public class DeviceIdleManager {
+    private final Context mContext;
+    private final IDeviceIdleController mService;
+
+    /**
+     * @hide
+     */
+    public DeviceIdleManager(@NonNull Context context, @NonNull IDeviceIdleController service) {
+        mContext = context;
+        mService = service;
+    }
+
+    IDeviceIdleController getService() {
+        return mService;
+    }
+
+    /**
+     * Ends any active idle session.
+     *
+     * @param reason The reason to end. Used for debugging purposes.
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void endIdle(@NonNull String reason) {
+        try {
+            mService.exitIdle(reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @return package names the system has white-listed to opt out of power save restrictions,
+     * except for device idle mode.
+     *
+     * @hide Should be migrated to PowerExemptionManager
+     */
+    @TestApi
+    public @NonNull String[] getSystemPowerWhitelistExceptIdle() {
+        try {
+            return mService.getSystemPowerWhitelistExceptIdle();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @return package names the system has white-listed to opt out of power save restrictions for
+     * all modes.
+     *
+     * @hide Should be migrated to PowerExemptionManager
+     */
+    @TestApi
+    public @NonNull String[] getSystemPowerWhitelist() {
+        try {
+            return mService.getSystemPowerWhitelist();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-34/android/os/DisplayPerfTest.java b/android-34/android/os/DisplayPerfTest.java
new file mode 100644
index 0000000..0cce6ad
--- /dev/null
+++ b/android-34/android/os/DisplayPerfTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.provider.Settings;
+import android.view.Display;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPerfTest {
+    private static final float DELTA = 0.001f;
+
+    @Rule
+    public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private DisplayManager mDisplayManager;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mDisplayManager = mContext.getSystemService(DisplayManager.class);
+    }
+
+    @Test
+    public void testBrightnessChanges() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+        SystemClock.sleep(20);
+        float brightness = 0.3f;
+        while (state.keepRunning()) {
+            setAndWaitToChangeBrightness(brightness);
+            brightness = toggleBrightness(brightness);
+        }
+    }
+
+    private float toggleBrightness(float oldBrightness) {
+        float[] brightnesses = new float[]{0.3f, 0.5f};
+        if (oldBrightness == brightnesses[0]) {
+            return brightnesses[1];
+        }
+        return brightnesses[0];
+    }
+
+    private void setAndWaitToChangeBrightness(float brightness) throws Exception {
+        mDisplayManager.setBrightness(0, brightness);
+        PollingCheck.check("Brightness is not set to the expected value", 500,
+                () -> isInRange(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY), brightness,
+                        DELTA));
+    }
+
+    private boolean isInRange(float value, float target, float delta) {
+        return target - delta <= value && target + delta >= value;
+    }
+}
diff --git a/android-34/android/os/DropBoxManager.java b/android-34/android/os/DropBoxManager.java
new file mode 100644
index 0000000..403f55c
--- /dev/null
+++ b/android-34/android/os/DropBoxManager.java
@@ -0,0 +1,410 @@
+/*
+ * 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.
+ * 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.os;
+
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_LOGS;
+
+import android.annotation.BytesLong;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.internal.os.IDropBoxManagerService;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Enqueues chunks of data (from various sources -- application crashes, kernel
+ * log records, etc.).  The queue is size bounded and will drop old data if the
+ * enqueued data exceeds the maximum size.  You can think of this as a
+ * persistent, system-wide, blob-oriented "logcat".
+ *
+ * <p>DropBoxManager entries are not sent anywhere directly, but other system
+ * services and debugging tools may scan and upload entries for processing.
+ */
+@SystemService(Context.DROPBOX_SERVICE)
+public class DropBoxManager {
+    private static final String TAG = "DropBoxManager";
+
+    private final Context mContext;
+    @UnsupportedAppUsage
+    private final IDropBoxManagerService mService;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "IS_" }, value = { IS_EMPTY, IS_TEXT, IS_GZIPPED })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flags {}
+
+    /** Flag value: Entry's content was deleted to save space. */
+    public static final int IS_EMPTY = 1;
+
+    /** Flag value: Content is human-readable UTF-8 text (can be combined with IS_GZIPPED). */
+    public static final int IS_TEXT = 2;
+
+    /** Flag value: Content can be decompressed with java.util.zip.GZIPOutputStream. */
+    public static final int IS_GZIPPED = 4;
+
+    /** Flag value for serialization only: Value is a byte array, not a file descriptor */
+    private static final int HAS_BYTE_ARRAY = 8;
+
+    /**
+     * Broadcast Action: This is broadcast when a new entry is added in the dropbox.
+     * You must hold the {@link android.Manifest.permission#READ_LOGS} permission
+     * in order to receive this broadcast. This broadcast can be rate limited for low priority
+     * entries
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DROPBOX_ENTRY_ADDED =
+        "android.intent.action.DROPBOX_ENTRY_ADDED";
+
+    /**
+     * Extra for {@link android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED}:
+     * string containing the dropbox tag.
+     */
+    public static final String EXTRA_TAG = "tag";
+
+    /**
+     * Extra for {@link android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED}:
+     * long integer value containing time (in milliseconds since January 1, 1970 00:00:00 UTC)
+     * when the entry was created.
+     */
+    public static final String EXTRA_TIME = "time";
+
+    /**
+     * Extra for {@link android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED}:
+     * integer value containing number of broadcasts dropped due to rate limiting on
+     * this {@link android.os.DropBoxManager#EXTRA_TAG}
+     */
+    public static final String EXTRA_DROPPED_COUNT = "android.os.extra.DROPPED_COUNT";
+
+    /**
+     * A single entry retrieved from the drop box.
+     * This may include a reference to a stream, so you must call
+     * {@link #close()} when you are done using it.
+     */
+    public static class Entry implements Parcelable, Closeable {
+        private final @NonNull String mTag;
+        private final @CurrentTimeMillisLong long mTimeMillis;
+
+        private final @Nullable byte[] mData;
+        private final @Nullable ParcelFileDescriptor mFileDescriptor;
+        private final @Flags int mFlags;
+
+        /** Create a new empty Entry with no contents. */
+        public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis) {
+            if (tag == null) throw new NullPointerException("tag == null");
+
+            mTag = tag;
+            mTimeMillis = millis;
+            mData = null;
+            mFileDescriptor = null;
+            mFlags = IS_EMPTY;
+        }
+
+        /** Create a new Entry with plain text contents. */
+        public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+                @NonNull String text) {
+            if (tag == null) throw new NullPointerException("tag == null");
+            if (text == null) throw new NullPointerException("text == null");
+
+            mTag = tag;
+            mTimeMillis = millis;
+            mData = text.getBytes(StandardCharsets.UTF_8);
+            mFileDescriptor = null;
+            mFlags = IS_TEXT;
+        }
+
+        /**
+         * Create a new Entry with byte array contents.
+         * The data array must not be modified after creating this entry.
+         */
+        public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+                @Nullable byte[] data, @Flags int flags) {
+            if (tag == null) throw new NullPointerException("tag == null");
+            if (((flags & IS_EMPTY) != 0) != (data == null)) {
+                throw new IllegalArgumentException("Bad flags: " + flags);
+            }
+
+            mTag = tag;
+            mTimeMillis = millis;
+            mData = data;
+            mFileDescriptor = null;
+            mFlags = flags;
+        }
+
+        /**
+         * Create a new Entry with streaming data contents.
+         * Takes ownership of the ParcelFileDescriptor.
+         */
+        public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+                @Nullable ParcelFileDescriptor data, @Flags int flags) {
+            if (tag == null) throw new NullPointerException("tag == null");
+            if (((flags & IS_EMPTY) != 0) != (data == null)) {
+                throw new IllegalArgumentException("Bad flags: " + flags);
+            }
+
+            mTag = tag;
+            mTimeMillis = millis;
+            mData = null;
+            mFileDescriptor = data;
+            mFlags = flags;
+        }
+
+        /**
+         * Create a new Entry with the contents read from a file.
+         * The file will be read when the entry's contents are requested.
+         */
+        public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+                @NonNull File data, @Flags int flags) throws IOException {
+            if (tag == null) throw new NullPointerException("tag == null");
+            if ((flags & IS_EMPTY) != 0) throw new IllegalArgumentException("Bad flags: " + flags);
+
+            mTag = tag;
+            mTimeMillis = millis;
+            mData = null;
+            mFileDescriptor = ParcelFileDescriptor.open(data, ParcelFileDescriptor.MODE_READ_ONLY);
+            mFlags = flags;
+        }
+
+        /** Close the input stream associated with this entry. */
+        public void close() {
+            try { if (mFileDescriptor != null) mFileDescriptor.close(); } catch (IOException e) { }
+        }
+
+        /** @return the tag originally attached to the entry. */
+        public @NonNull String getTag() {
+            return mTag;
+        }
+
+        /** @return time when the entry was originally created. */
+        public @CurrentTimeMillisLong long getTimeMillis() {
+            return mTimeMillis;
+        }
+
+        /** @return flags describing the content returned by {@link #getInputStream()}. */
+        public @Flags int getFlags() {
+            // getInputStream() decompresses.
+            return mFlags & ~IS_GZIPPED;
+        }
+
+        /**
+         * @param maxBytes of string to return (will truncate at this length).
+         * @return the uncompressed text contents of the entry, null if the entry is not text.
+         */
+        public @Nullable String getText(@BytesLong int maxBytes) {
+            if ((mFlags & IS_TEXT) == 0) return null;
+            if (mData != null) return new String(mData, 0, Math.min(maxBytes, mData.length));
+
+            InputStream is = null;
+            try {
+                is = getInputStream();
+                if (is == null) return null;
+                byte[] buf = new byte[maxBytes];
+                int readBytes = 0;
+                int n = 0;
+                while (n >= 0 && (readBytes += n) < maxBytes) {
+                    n = is.read(buf, readBytes, maxBytes - readBytes);
+                }
+                return new String(buf, 0, readBytes);
+            } catch (IOException e) {
+                return null;
+            } finally {
+                try { if (is != null) is.close(); } catch (IOException e) {}
+            }
+        }
+
+        /** @return the uncompressed contents of the entry, or null if the contents were lost */
+        public @Nullable InputStream getInputStream() throws IOException {
+            InputStream is;
+            if (mData != null) {
+                is = new ByteArrayInputStream(mData);
+            } else if (mFileDescriptor != null) {
+                is = new ParcelFileDescriptor.AutoCloseInputStream(mFileDescriptor);
+            } else {
+                return null;
+            }
+            return (mFlags & IS_GZIPPED) != 0
+                ? new GZIPInputStream(new BufferedInputStream(is)) : is;
+        }
+
+        public static final @android.annotation.NonNull Parcelable.Creator<Entry> CREATOR = new Parcelable.Creator() {
+            public Entry[] newArray(int size) { return new Entry[size]; }
+            public Entry createFromParcel(Parcel in) {
+                String tag = in.readString();
+                long millis = in.readLong();
+                int flags = in.readInt();
+                if ((flags & HAS_BYTE_ARRAY) != 0) {
+                    return new Entry(tag, millis, in.createByteArray(), flags & ~HAS_BYTE_ARRAY);
+                } else {
+                    ParcelFileDescriptor pfd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+                    return new Entry(tag, millis, pfd, flags);
+                }
+            }
+        };
+
+        public int describeContents() {
+            return mFileDescriptor != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(mTag);
+            out.writeLong(mTimeMillis);
+            if (mFileDescriptor != null) {
+                out.writeInt(mFlags & ~HAS_BYTE_ARRAY);  // Clear bit just to be safe
+                mFileDescriptor.writeToParcel(out, flags);
+            } else {
+                out.writeInt(mFlags | HAS_BYTE_ARRAY);
+                out.writeByteArray(mData);
+            }
+        }
+    }
+
+    /** {@hide} */
+    public DropBoxManager(Context context, IDropBoxManagerService service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Create an instance for testing. All methods will fail unless
+     * overridden with an appropriate mock implementation.  To obtain a
+     * functional instance, use {@link android.content.Context#getSystemService}.
+     */
+    protected DropBoxManager() {
+        mContext = null;
+        mService = null;
+    }
+
+    /**
+     * Stores human-readable text.  The data may be discarded eventually (or even
+     * immediately) if space is limited, or ignored entirely if the tag has been
+     * blocked (see {@link #isTagEnabled}).
+     *
+     * @param tag describing the type of entry being stored
+     * @param data value to store
+     */
+    public void addText(@NonNull String tag, @NonNull String data) {
+        addData(tag, data.getBytes(StandardCharsets.UTF_8), IS_TEXT);
+    }
+
+    /**
+     * Stores binary data, which may be ignored or discarded as with {@link #addText}.
+     *
+     * @param tag describing the type of entry being stored
+     * @param data value to store
+     * @param flags describing the data
+     */
+    public void addData(@NonNull String tag, @Nullable byte[] data, @Flags int flags) {
+        if (data == null) throw new NullPointerException("data == null");
+        try {
+            mService.addData(tag, data, flags);
+        } catch (RemoteException e) {
+            if (e instanceof TransactionTooLargeException
+                    && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+                Log.e(TAG, "App sent too much data, so it was ignored", e);
+                return;
+            }
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Stores the contents of a file, which may be ignored or discarded as with
+     * {@link #addText}.
+     *
+     * @param tag describing the type of entry being stored
+     * @param file to read from
+     * @param flags describing the data
+     * @throws IOException if the file can't be opened
+     */
+    public void addFile(@NonNull String tag, @NonNull File file, @Flags int flags)
+            throws IOException {
+        if (file == null) throw new NullPointerException("file == null");
+        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
+                ParcelFileDescriptor.MODE_READ_ONLY)) {
+            mService.addFile(tag, pfd, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks any blacklists (set in system settings) to see whether a certain
+     * tag is allowed.  Entries with disabled tags will be dropped immediately,
+     * so you can save the work of actually constructing and sending the data.
+     *
+     * @param tag that would be used in {@link #addText} or {@link #addFile}
+     * @return whether events with that tag would be accepted
+     */
+    public boolean isTagEnabled(String tag) {
+        try {
+            return mService.isTagEnabled(tag);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the next entry from the drop box <em>after</em> the specified time.
+     * You must always call {@link Entry#close()} on the return value!
+     *
+     * @param tag of entry to look for, null for all tags
+     * @param msec time of the last entry seen
+     * @return the next entry, or null if there are no more entries
+     */
+    @RequiresPermission(allOf = { READ_LOGS, PACKAGE_USAGE_STATS })
+    public @Nullable Entry getNextEntry(String tag, long msec) {
+        try {
+            return mService.getNextEntryWithAttribution(tag, msec, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (SecurityException e) {
+            if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+                throw e;
+            } else {
+                Log.w(TAG, e.getMessage());
+                return null;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    // TODO: It may be useful to have some sort of notification mechanism
+    // when data is added to the dropbox, for demand-driven readers --
+    // for now readers need to poll the dropbox to find new data.
+}
diff --git a/android-34/android/os/Environment.java b/android-34/android/os/Environment.java
new file mode 100644
index 0000000..536ef31
--- /dev/null
+++ b/android-34/android/os/Environment.java
@@ -0,0 +1,1597 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Provides access to environment variables.
+ */
+public class Environment {
+    private static final String TAG = "Environment";
+
+    // NOTE: keep credential-protected paths in sync with StrictMode.java
+
+    private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
+    private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+    private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
+    private static final String ENV_ANDROID_EXPAND = "ANDROID_EXPAND";
+    private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
+    private static final String ENV_DOWNLOAD_CACHE = "DOWNLOAD_CACHE";
+    private static final String ENV_OEM_ROOT = "OEM_ROOT";
+    private static final String ENV_ODM_ROOT = "ODM_ROOT";
+    private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
+    private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT";
+    private static final String ENV_SYSTEM_EXT_ROOT = "SYSTEM_EXT_ROOT";
+    private static final String ENV_APEX_ROOT = "APEX_ROOT";
+
+    /** {@hide} */
+    public static final String DIR_ANDROID = "Android";
+    private static final String DIR_DATA = "data";
+    private static final String DIR_MEDIA = "media";
+    private static final String DIR_OBB = "obb";
+    private static final String DIR_FILES = "files";
+    private static final String DIR_CACHE = "cache";
+
+    /**
+     * The folder name prefix for the user credential protected data directory. This is exposed for
+     * use in string path caching for {@link ApplicationInfo} objects, and should not be accessed
+     * directly otherwise. Prefer {@link #getDataUserCeDirectory(String, int)}.
+     * {@hide}
+     */
+    public static final String DIR_USER_CE = "user";
+
+    /**
+     * The folder name prefix for the user device protected data directory. This is exposed for use
+     * in string path caching for {@link ApplicationInfo} objects, and should not be accessed
+     * directly otherwise. Prefer {@link #getDataUserDeDirectory(String, int)}.
+     * {@hide}
+     */
+    public static final String DIR_USER_DE = "user_de";
+
+    /** {@hide} */
+    @Deprecated
+    public static final String DIRECTORY_ANDROID = DIR_ANDROID;
+
+    private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+    private static final String DIR_ANDROID_DATA_PATH = getDirectoryPath(ENV_ANDROID_DATA, "/data");
+    private static final File DIR_ANDROID_DATA = new File(DIR_ANDROID_DATA_PATH);
+    private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand");
+    private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
+    private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache");
+    private static final File DIR_METADATA = new File("/metadata");
+    private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
+    private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm");
+    private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
+    private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
+    private static final File DIR_SYSTEM_EXT_ROOT = getDirectory(ENV_SYSTEM_EXT_ROOT,
+            "/system_ext");
+    private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
+            "/apex");
+
+    /**
+     * Scoped Storage is on by default. However, it is not strictly enforced and there are multiple
+     * ways to opt out of scoped storage:
+     * <ul>
+     * <li>Target Sdk < Q</li>
+     * <li>Target Sdk = Q and has `requestLegacyExternalStorage` set in AndroidManifest.xml</li>
+     * <li>Target Sdk > Q: Upgrading from an app that was opted out of scoped storage and has
+     * `preserveLegacyExternalStorage` set in AndroidManifest.xml</li>
+     * </ul>
+     * This flag is enabled for all apps by default as Scoped Storage is enabled by default.
+     * Developers can disable this flag to opt out of Scoped Storage and have legacy storage
+     * workflow.
+     *
+     * Note: {@code FORCE_ENABLE_SCOPED_STORAGE} should also be disabled for apps to opt out of
+     * scoped storage.
+     * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+     * Any modifications to this flag should be reflected there as well.
+     * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+     */
+    @ChangeId
+    private static final long DEFAULT_SCOPED_STORAGE = 149924527L;
+
+    /**
+     * See definition in com.android.providers.media.LocalCallingIdentity
+     */
+    /**
+     * Setting this flag strictly enforces Scoped Storage regardless of:
+     * <ul>
+     * <li>The value of Target Sdk</li>
+     * <li>The value of `requestLegacyExternalStorage` in AndroidManifest.xml</li>
+     * <li>The value of `preserveLegacyExternalStorage` in AndroidManifest.xml</li>
+     * </ul>
+     *
+     * Note: {@code DEFAULT_SCOPED_STORAGE} should also be enabled for apps to be enforced into
+     * scoped storage.
+     * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+     * Any modifications to this flag should be reflected there as well.
+     * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+     */
+    @ChangeId
+    @Disabled
+    private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L;
+
+    @UnsupportedAppUsage
+    private static UserEnvironment sCurrentUser;
+    private static boolean sUserRequired;
+
+    static {
+        initForCurrentUser();
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public static void initForCurrentUser() {
+        final int userId = UserHandle.myUserId();
+        sCurrentUser = new UserEnvironment(userId);
+    }
+
+    /** {@hide} */
+    public static class UserEnvironment {
+        private final int mUserId;
+
+        @UnsupportedAppUsage
+        public UserEnvironment(int userId) {
+            mUserId = userId;
+        }
+
+        @UnsupportedAppUsage
+        public File[] getExternalDirs() {
+            final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId,
+                    StorageManager.FLAG_FOR_WRITE);
+            final File[] files = new File[volumes.length];
+            for (int i = 0; i < volumes.length; i++) {
+                files[i] = volumes[i].getPathFile();
+            }
+            return files;
+        }
+
+        @UnsupportedAppUsage
+        public File getExternalStorageDirectory() {
+            return getExternalDirs()[0];
+        }
+
+        @UnsupportedAppUsage
+        public File getExternalStoragePublicDirectory(String type) {
+            return buildExternalStoragePublicDirs(type)[0];
+        }
+
+        public File[] buildExternalStoragePublicDirs(String type) {
+            return buildPaths(getExternalDirs(), type);
+        }
+
+        public File[] buildExternalStorageAndroidDataDirs() {
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA);
+        }
+
+        public File[] buildExternalStorageAndroidObbDirs() {
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB);
+        }
+
+        public File[] buildExternalStorageAppDataDirs(String packageName) {
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName);
+        }
+
+        public File[] buildExternalStorageAppMediaDirs(String packageName) {
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_MEDIA, packageName);
+        }
+
+        public File[] buildExternalStorageAppObbDirs(String packageName) {
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB, packageName);
+        }
+
+        public File[] buildExternalStorageAppFilesDirs(String packageName) {
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
+        }
+
+        public File[] buildExternalStorageAppCacheDirs(String packageName) {
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
+        }
+    }
+
+    /**
+     * Return root of the "system" partition holding the core Android OS.
+     * Always present and mounted read-only.
+     */
+    public static @NonNull File getRootDirectory() {
+        return DIR_ANDROID_ROOT;
+    }
+
+    /**
+     * Return root directory where all external storage devices will be mounted.
+     * For example, {@link #getExternalStorageDirectory()} will appear under
+     * this location.
+     */
+    public static @NonNull File getStorageDirectory() {
+        return DIR_ANDROID_STORAGE;
+    }
+
+    /**
+     * Return root directory of the "oem" partition holding OEM customizations,
+     * if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull File getOemDirectory() {
+        return DIR_OEM_ROOT;
+    }
+
+    /**
+     * Return root directory of the "odm" partition holding ODM customizations,
+     * if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull File getOdmDirectory() {
+        return DIR_ODM_ROOT;
+    }
+
+    /**
+     * Return root directory of the "vendor" partition that holds vendor-provided
+     * software that should persist across simple reflashing of the "system" partition.
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull File getVendorDirectory() {
+        return DIR_VENDOR_ROOT;
+    }
+
+    /**
+     * Return root directory of the "product" partition holding product-specific
+     * customizations if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull File getProductDirectory() {
+        return DIR_PRODUCT_ROOT;
+    }
+
+    /**
+     * Return root directory of the "product_services" partition holding middleware
+     * services if any. If present, the partition is mounted read-only.
+     *
+     * @deprecated This directory is not guaranteed to exist.
+     *             Its name is changed to "system_ext" because the partition's purpose is changed.
+     *             {@link #getSystemExtDirectory()}
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    public static @NonNull File getProductServicesDirectory() {
+        return getDirectory("PRODUCT_SERVICES_ROOT", "/product_services");
+    }
+
+    /**
+     * Return root directory of the "system_ext" partition holding system partition's extension
+     * If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull File getSystemExtDirectory() {
+        return DIR_SYSTEM_EXT_ROOT;
+    }
+
+    /**
+     * Return root directory of the apex mount point, where all the apex modules are made available
+     * to the rest of the system.
+     *
+     * @hide
+     */
+    public static @NonNull File getApexDirectory() {
+        return DIR_APEX_ROOT;
+    }
+
+    /**
+     * Return the system directory for a user. This is for use by system
+     * services to store files relating to the user. This directory will be
+     * automatically deleted when the user is removed.
+     *
+     * @deprecated This directory is valid and still exists, but but callers
+     *             should <em>strongly</em> consider switching to using either
+     *             {@link #getDataSystemCeDirectory(int)} or
+     *             {@link #getDataSystemDeDirectory(int)}, both of which support
+     *             fast user wipe.
+     * @hide
+     */
+    @Deprecated
+    public static File getUserSystemDirectory(int userId) {
+        return new File(new File(getDataSystemDirectory(), "users"), Integer.toString(userId));
+    }
+
+    /**
+     * Returns the config directory for a user. This is for use by system
+     * services to store files relating to the user which should be readable by
+     * any app running as that user.
+     *
+     * @deprecated This directory is valid and still exists, but callers should
+     *             <em>strongly</em> consider switching to
+     *             {@link #getDataMiscCeDirectory(int)} which is protected with
+     *             user credentials or {@link #getDataMiscDeDirectory(int)}
+     *             which supports fast user wipe.
+     * @hide
+     */
+    @Deprecated
+    public static File getUserConfigDirectory(int userId) {
+        return new File(new File(new File(
+                getDataDirectory(), "misc"), "user"), Integer.toString(userId));
+    }
+
+    /**
+     * Return the user data directory.
+     */
+    public static File getDataDirectory() {
+        return DIR_ANDROID_DATA;
+    }
+
+    /**
+     * @see #getDataDirectory()
+     * @hide
+     */
+    public static String getDataDirectoryPath() {
+        return DIR_ANDROID_DATA_PATH;
+    }
+
+    /** {@hide} */
+    public static File getDataDirectory(String volumeUuid) {
+        if (TextUtils.isEmpty(volumeUuid)) {
+            return DIR_ANDROID_DATA;
+        } else {
+            return new File("/mnt/expand/" + volumeUuid);
+        }
+    }
+
+    /** @hide */
+    public static String getDataDirectoryPath(String volumeUuid) {
+        if (TextUtils.isEmpty(volumeUuid)) {
+            return DIR_ANDROID_DATA_PATH;
+        } else {
+            return getExpandDirectory().getAbsolutePath() + File.separator + volumeUuid;
+        }
+    }
+
+    /** {@hide} */
+    public static File getExpandDirectory() {
+        return DIR_ANDROID_EXPAND;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public static File getDataSystemDirectory() {
+        return new File(getDataDirectory(), "system");
+    }
+
+    /**
+     * Returns the base directory for per-user system directory, device encrypted.
+     * {@hide}
+     */
+    public static File getDataSystemDeDirectory() {
+        return buildPath(getDataDirectory(), "system_de");
+    }
+
+    /**
+     * Returns the base directory for per-user system directory, credential encrypted.
+     * {@hide}
+     */
+    public static File getDataSystemCeDirectory() {
+        return buildPath(getDataDirectory(), "system_ce");
+    }
+
+    /**
+     * Return the "credential encrypted" system directory for a user. This is
+     * for use by system services to store files relating to the user. This
+     * directory supports fast user wipe, and will be automatically deleted when
+     * the user is removed.
+     * <p>
+     * Data stored under this path is "credential encrypted", which uses an
+     * encryption key that is entangled with user credentials, such as a PIN or
+     * password. The contents will only be available once the user has been
+     * unlocked, as reported by {@code SystemService.onUnlockUser()}.
+     * <p>
+     * New code should <em>strongly</em> prefer storing sensitive data in these
+     * credential encrypted areas.
+     *
+     * @hide
+     */
+    public static File getDataSystemCeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
+    }
+
+    /**
+     * Return the "device encrypted" system directory for a user. This is for
+     * use by system services to store files relating to the user. This
+     * directory supports fast user wipe, and will be automatically deleted when
+     * the user is removed.
+     * <p>
+     * Data stored under this path is "device encrypted", which uses an
+     * encryption key that is tied to the physical device. The contents will
+     * only be available once the device has finished a {@code dm-verity}
+     * protected boot.
+     * <p>
+     * New code should <em>strongly</em> avoid storing sensitive data in these
+     * device encrypted areas.
+     *
+     * @hide
+     */
+    public static File getDataSystemDeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "system_de", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataMiscDirectory() {
+        return new File(getDataDirectory(), "misc");
+    }
+
+    /** {@hide} */
+    public static File getDataMiscCeDirectory() {
+        return buildPath(getDataDirectory(), "misc_ce");
+    }
+
+    /** {@hide} */
+    public static File getDataMiscCeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "misc_ce", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    private static File getDataMiscCeDirectory(String volumeUuid, int userId) {
+        return buildPath(getDataDirectory(volumeUuid), "misc_ce", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataMiscCeSharedSdkSandboxDirectory(String volumeUuid, int userId,
+            String packageName) {
+        return buildPath(getDataMiscCeDirectory(volumeUuid, userId), "sdksandbox",
+                packageName, "shared");
+    }
+
+    /** {@hide} */
+    public static File getDataMiscDeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "misc_de", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    private static File getDataMiscDeDirectory(String volumeUuid, int userId) {
+        return buildPath(getDataDirectory(volumeUuid), "misc_de", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataMiscDeSharedSdkSandboxDirectory(String volumeUuid, int userId,
+            String packageName) {
+        return buildPath(getDataMiscDeDirectory(volumeUuid, userId), "sdksandbox",
+                packageName, "shared");
+    }
+
+    private static File getDataProfilesDeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "misc", "profiles", "cur", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataVendorCeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "vendor_ce", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataVendorDeDirectory(int userId) {
+        return buildPath(getDataDirectory(), "vendor_de", String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    public static File getDataRefProfilesDePackageDirectory(String packageName) {
+        return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
+    }
+
+    /** {@hide} */
+    public static File getDataProfilesDePackageDirectory(int userId, String packageName) {
+        return buildPath(getDataProfilesDeDirectory(userId), packageName);
+    }
+
+    /** {@hide} */
+    public static File getDataAppDirectory(String volumeUuid) {
+        return new File(getDataDirectory(volumeUuid), "app");
+    }
+
+    /** {@hide} */
+    public static File getDataStagingDirectory(String volumeUuid) {
+        return new File(getDataDirectory(volumeUuid), "app-staging");
+    }
+
+    /** {@hide} */
+    public static File getDataUserCeDirectory(String volumeUuid) {
+        return new File(getDataDirectory(volumeUuid), DIR_USER_CE);
+    }
+
+    /** {@hide} */
+    public static File getDataUserCeDirectory(String volumeUuid, int userId) {
+        return new File(getDataUserCeDirectory(volumeUuid), String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    @NonNull
+    public static File getDataUserCePackageDirectory(@Nullable String volumeUuid, int userId,
+            @NonNull String packageName) {
+        // TODO: keep consistent with installd
+        return new File(getDataUserCeDirectory(volumeUuid, userId), packageName);
+    }
+
+    /**
+     * Retrieve the credential encrypted data directory for a specific package of a specific user.
+     * This is equivalent to {@link ApplicationInfo#credentialProtectedDataDir}, exposed because
+     * fetching a full {@link ApplicationInfo} instance may be expensive if all the caller needs
+     * is this directory.
+     *
+     * @param storageUuid The storage volume for this directory, usually retrieved from a
+     * {@link StorageManager} API or {@link ApplicationInfo#storageUuid}.
+     * @param user The user this directory is for.
+     * @param packageName The app this directory is for.
+     *
+     * @see ApplicationInfo#credentialProtectedDataDir
+     * @return A file to the directory.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public static File getDataCePackageDirectoryForUser(@NonNull UUID storageUuid,
+            @NonNull UserHandle user, @NonNull String packageName) {
+        var volumeUuid = StorageManager.convert(storageUuid);
+        return getDataUserCePackageDirectory(volumeUuid, user.getIdentifier(), packageName);
+    }
+
+    /** {@hide} */
+    public static File getDataUserDeDirectory(String volumeUuid) {
+        return new File(getDataDirectory(volumeUuid), DIR_USER_DE);
+    }
+
+    /** {@hide} */
+    public static File getDataUserDeDirectory(String volumeUuid, int userId) {
+        return new File(getDataUserDeDirectory(volumeUuid), String.valueOf(userId));
+    }
+
+    /** {@hide} */
+    @NonNull
+    public static File getDataUserDePackageDirectory(@Nullable String volumeUuid, int userId,
+            @NonNull String packageName) {
+        // TODO: keep consistent with installd
+        return new File(getDataUserDeDirectory(volumeUuid, userId), packageName);
+    }
+
+    /**
+     * Retrieve the device encrypted data directory for a specific package of a specific user. This
+     * is equivalent to {@link ApplicationInfo#deviceProtectedDataDir}, exposed because fetching a
+     * full {@link ApplicationInfo} instance may be expensive if all the caller needs is this
+     * directory.
+     *
+     * @param storageUuid The storage volume for this directory, usually retrieved from a
+     * {@link StorageManager} API or {@link ApplicationInfo#storageUuid}.
+     * @param user The user this directory is for.
+     * @param packageName The app this directory is for.
+     *
+     * @see ApplicationInfo#deviceProtectedDataDir
+     * @return A file to the directory.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public static File getDataDePackageDirectoryForUser(@NonNull UUID storageUuid,
+            @NonNull UserHandle user, @NonNull String packageName) {
+        var volumeUuid = StorageManager.convert(storageUuid);
+        return getDataUserDePackageDirectory(volumeUuid, user.getIdentifier(), packageName);
+    }
+
+    /**
+     * Return preloads directory.
+     * <p>This directory may contain pre-loaded content such as
+     * {@link #getDataPreloadsDemoDirectory() demo videos} and
+     * {@link #getDataPreloadsAppsDirectory() APK files} .
+     * {@hide}
+     */
+    public static File getDataPreloadsDirectory() {
+        return new File(getDataDirectory(), "preloads");
+    }
+
+    /**
+     * @see #getDataPreloadsDirectory()
+     * {@hide}
+     */
+    public static File getDataPreloadsDemoDirectory() {
+        return new File(getDataPreloadsDirectory(), "demo");
+    }
+
+    /**
+     * @see #getDataPreloadsDirectory()
+     * {@hide}
+     */
+    public static File getDataPreloadsAppsDirectory() {
+        return new File(getDataPreloadsDirectory(), "apps");
+    }
+
+    /**
+     * @see #getDataPreloadsDirectory()
+     * {@hide}
+     */
+    public static File getDataPreloadsMediaDirectory() {
+        return new File(getDataPreloadsDirectory(), "media");
+    }
+
+    /**
+     * Returns location of preloaded cache directory for package name
+     * @see #getDataPreloadsDirectory()
+     * {@hide}
+     */
+    public static File getDataPreloadsFileCacheDirectory(String packageName) {
+        return new File(getDataPreloadsFileCacheDirectory(), packageName);
+    }
+
+    /**
+     * Returns location of preloaded cache directory.
+     * @see #getDataPreloadsDirectory()
+     * {@hide}
+     */
+    public static File getDataPreloadsFileCacheDirectory() {
+        return new File(getDataPreloadsDirectory(), "file_cache");
+    }
+
+    /**
+     * Returns location of packages cache directory.
+     * {@hide}
+     */
+    public static File getPackageCacheDirectory() {
+        return new File(getDataSystemDirectory(), "package_cache");
+    }
+
+    /**
+     * Return locations where media files (such as ringtones, notification
+     * sounds, or alarm sounds) may be located on internal storage. These are
+     * typically indexed under {@link MediaStore#VOLUME_INTERNAL}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull Collection<File> getInternalMediaDirectories() {
+        final ArrayList<File> res = new ArrayList<>();
+        addCanonicalFile(res, new File(Environment.getRootDirectory(), "media"));
+        addCanonicalFile(res, new File(Environment.getOemDirectory(), "media"));
+        addCanonicalFile(res, new File(Environment.getProductDirectory(), "media"));
+        return res;
+    }
+
+    private static void addCanonicalFile(List<File> list, File file) {
+        try {
+            list.add(file.getCanonicalFile());
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to resolve " + file + ": " + e);
+            list.add(file);
+        }
+    }
+
+    /**
+     * Return the primary shared/external storage directory. This directory may
+     * not currently be accessible if it has been mounted by the user on their
+     * computer, has been removed from the device, or some other problem has
+     * happened. You can determine its current state with
+     * {@link #getExternalStorageState()}.
+     * <p>
+     * <em>Note: don't be confused by the word "external" here. This directory
+     * can better be thought as media/shared storage. It is a filesystem that
+     * can hold a relatively large amount of data and that is shared across all
+     * applications (does not enforce permissions). Traditionally this is an SD
+     * card, but it may also be implemented as built-in storage in a device that
+     * is distinct from the protected internal storage and can be mounted as a
+     * filesystem on a computer.</em>
+     * <p>
+     * On devices with multiple users (as described by {@link UserManager}),
+     * each user has their own isolated shared storage. Applications only have
+     * access to the shared storage for the user they're running as.
+     * <p>
+     * In devices with multiple shared/external storage directories, this
+     * directory represents the primary storage that the user will interact
+     * with. Access to secondary storage is available through
+     * {@link Context#getExternalFilesDirs(String)},
+     * {@link Context#getExternalCacheDirs()}, and
+     * {@link Context#getExternalMediaDirs()}.
+     * <p>
+     * Applications should not directly use this top-level directory, in order
+     * to avoid polluting the user's root namespace. Any files that are private
+     * to the application should be placed in a directory returned by
+     * {@link android.content.Context#getExternalFilesDir
+     * Context.getExternalFilesDir}, which the system will take care of deleting
+     * if the application is uninstalled. Other shared files should be placed in
+     * one of the directories returned by
+     * {@link #getExternalStoragePublicDirectory}.
+     * <p>
+     * Writing to this path requires the
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission,
+     * and starting in {@link android.os.Build.VERSION_CODES#KITKAT}, read
+     * access requires the
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission,
+     * which is automatically granted if you hold the write permission.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, if your
+     * application only needs to store internal data, consider using
+     * {@link Context#getExternalFilesDir(String)},
+     * {@link Context#getExternalCacheDir()}, or
+     * {@link Context#getExternalMediaDirs()}, which require no permissions to
+     * read or write.
+     * <p>
+     * This path may change between platform versions, so applications should
+     * only persist relative paths.
+     * <p>
+     * Here is an example of typical code to monitor the state of external
+     * storage:
+     * <p>
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * monitor_storage}
+     * <p>
+     * Note that alternatives such as {@link Context#getExternalFilesDir(String)} or
+     * {@link MediaStore} offer better performance.
+     *
+     * @see #getExternalStorageState()
+     * @see #isExternalStorageRemovable()
+     */
+    public static File getExternalStorageDirectory() {
+        throwIfUserRequired();
+        return sCurrentUser.getExternalDirs()[0];
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public static File getLegacyExternalStorageDirectory() {
+        return new File(System.getenv(ENV_EXTERNAL_STORAGE));
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static File getLegacyExternalStorageObbDirectory() {
+        return buildPath(getLegacyExternalStorageDirectory(), DIR_ANDROID, DIR_OBB);
+    }
+
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the regular list of music for the user.
+     * This may be combined with {@link #DIRECTORY_AUDIOBOOKS},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
+     */
+    public static String DIRECTORY_MUSIC = "Music";
+
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of podcasts that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
+     */
+    public static String DIRECTORY_PODCASTS = "Podcasts";
+
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of ringtones that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
+     */
+    public static String DIRECTORY_RINGTONES = "Ringtones";
+
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of alarms that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_RINGTONES},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
+     */
+    public static String DIRECTORY_ALARMS = "Alarms";
+
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of notifications that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
+     */
+    public static String DIRECTORY_NOTIFICATIONS = "Notifications";
+
+    /**
+     * Standard directory in which to place pictures that are available to
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, as the media scanner will find and collect pictures
+     * in any directory.
+     */
+    public static String DIRECTORY_PICTURES = "Pictures";
+
+    /**
+     * Standard directory in which to place movies that are available to
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, as the media scanner will find and collect movies
+     * in any directory.
+     */
+    public static String DIRECTORY_MOVIES = "Movies";
+
+    /**
+     * Standard directory in which to place files that have been downloaded by
+     * the user.  Note that this is primarily a convention for the top-level
+     * public directory, you are free to download files anywhere in your own
+     * private directories.  Also note that though the constant here is
+     * named DIRECTORY_DOWNLOADS (plural), the actual file name is non-plural for
+     * backwards compatibility reasons.
+     */
+    public static String DIRECTORY_DOWNLOADS = "Download";
+
+    /**
+     * The traditional location for pictures and videos when mounting the
+     * device as a camera.  Note that this is primarily a convention for the
+     * top-level public directory, as this convention makes no sense elsewhere.
+     */
+    public static String DIRECTORY_DCIM = "DCIM";
+
+    /**
+     * Standard directory in which to place documents that have been created by
+     * the user.
+     */
+    public static String DIRECTORY_DOCUMENTS = "Documents";
+
+    /**
+     * Standard directory in which to place screenshots that have been taken by
+     * the user. Typically used as a secondary directory under
+     * {@link #DIRECTORY_PICTURES}.
+     */
+    public static String DIRECTORY_SCREENSHOTS = "Screenshots";
+
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of audiobooks that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
+     */
+    public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
+
+    /**
+     * Standard directory in which to place any audio files that should be
+     * in the list of voice recordings recorded by voice recorder apps that
+     * the user can select (not as regular music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+     * and {@link #DIRECTORY_RINGTONES} as a series of directories
+     * to categorize a particular audio file as more than one type.
+     */
+    @NonNull
+    // The better way is that expose a static method getRecordingDirectories.
+    // But since it's an existing API surface and developers already
+    // used to DIRECTORY_* constants, we should keep using this pattern
+    // for consistency. We use SuppressLint here to avoid exposing a final
+    // field. A final field will prevent us from ever changing the value of
+    // DIRECTORY_RECORDINGS. Not that it's likely that we will ever need to
+    // change it, but it's better to have such option.
+    @SuppressLint({"MutableBareField", "AllUpper"})
+    public static String DIRECTORY_RECORDINGS = "Recordings";
+
+    /**
+     * List of standard storage directories.
+     * <p>
+     * Each of its values have its own constant:
+     * <ul>
+     *   <li>{@link #DIRECTORY_MUSIC}
+     *   <li>{@link #DIRECTORY_PODCASTS}
+     *   <li>{@link #DIRECTORY_ALARMS}
+     *   <li>{@link #DIRECTORY_RINGTONES}
+     *   <li>{@link #DIRECTORY_NOTIFICATIONS}
+     *   <li>{@link #DIRECTORY_PICTURES}
+     *   <li>{@link #DIRECTORY_MOVIES}
+     *   <li>{@link #DIRECTORY_DOWNLOADS}
+     *   <li>{@link #DIRECTORY_DCIM}
+     *   <li>{@link #DIRECTORY_DOCUMENTS}
+     *   <li>{@link #DIRECTORY_AUDIOBOOKS}
+     *   <li>{@link #DIRECTORY_RECORDINGS}
+     * </ul>
+     * @hide
+     */
+    public static final String[] STANDARD_DIRECTORIES = {
+            DIRECTORY_MUSIC,
+            DIRECTORY_PODCASTS,
+            DIRECTORY_RINGTONES,
+            DIRECTORY_ALARMS,
+            DIRECTORY_NOTIFICATIONS,
+            DIRECTORY_PICTURES,
+            DIRECTORY_MOVIES,
+            DIRECTORY_DOWNLOADS,
+            DIRECTORY_DCIM,
+            DIRECTORY_DOCUMENTS,
+            DIRECTORY_AUDIOBOOKS,
+            DIRECTORY_RECORDINGS,
+    };
+
+    /**
+     * @hide
+     */
+    public static boolean isStandardDirectory(String dir) {
+        for (String valid : STANDARD_DIRECTORIES) {
+            if (valid.equals(dir)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** {@hide} */ public static final int HAS_MUSIC = 1 << 0;
+    /** {@hide} */ public static final int HAS_PODCASTS = 1 << 1;
+    /** {@hide} */ public static final int HAS_RINGTONES = 1 << 2;
+    /** {@hide} */ public static final int HAS_ALARMS = 1 << 3;
+    /** {@hide} */ public static final int HAS_NOTIFICATIONS = 1 << 4;
+    /** {@hide} */ public static final int HAS_PICTURES = 1 << 5;
+    /** {@hide} */ public static final int HAS_MOVIES = 1 << 6;
+    /** {@hide} */ public static final int HAS_DOWNLOADS = 1 << 7;
+    /** {@hide} */ public static final int HAS_DCIM = 1 << 8;
+    /** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
+    /** {@hide} */ public static final int HAS_AUDIOBOOKS = 1 << 10;
+    /** {@hide} */ public static final int HAS_RECORDINGS = 1 << 11;
+
+    /** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
+    /** {@hide} */ public static final int HAS_OTHER = 1 << 17;
+
+    /**
+     * Classify the content types present on the given external storage device.
+     * <p>
+     * This is typically useful for deciding if an inserted SD card is empty, or
+     * if it contains content like photos that should be preserved.
+     *
+     * @hide
+     */
+    public static int classifyExternalStorageDirectory(File dir) {
+        int res = 0;
+        for (File f : FileUtils.listFilesOrEmpty(dir)) {
+            if (f.isFile() && isInterestingFile(f)) {
+                res |= HAS_OTHER;
+            } else if (f.isDirectory() && hasInterestingFiles(f)) {
+                final String name = f.getName();
+                if (DIRECTORY_MUSIC.equals(name)) res |= HAS_MUSIC;
+                else if (DIRECTORY_PODCASTS.equals(name)) res |= HAS_PODCASTS;
+                else if (DIRECTORY_RINGTONES.equals(name)) res |= HAS_RINGTONES;
+                else if (DIRECTORY_ALARMS.equals(name)) res |= HAS_ALARMS;
+                else if (DIRECTORY_NOTIFICATIONS.equals(name)) res |= HAS_NOTIFICATIONS;
+                else if (DIRECTORY_PICTURES.equals(name)) res |= HAS_PICTURES;
+                else if (DIRECTORY_MOVIES.equals(name)) res |= HAS_MOVIES;
+                else if (DIRECTORY_DOWNLOADS.equals(name)) res |= HAS_DOWNLOADS;
+                else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
+                else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
+                else if (DIRECTORY_AUDIOBOOKS.equals(name)) res |= HAS_AUDIOBOOKS;
+                else if (DIRECTORY_RECORDINGS.equals(name)) res |= HAS_RECORDINGS;
+                else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
+                else res |= HAS_OTHER;
+            }
+        }
+        return res;
+    }
+
+    private static boolean hasInterestingFiles(File dir) {
+        final ArrayDeque<File> explore = new ArrayDeque<>();
+        explore.add(dir);
+        while (!explore.isEmpty()) {
+            dir = explore.pop();
+            for (File f : FileUtils.listFilesOrEmpty(dir)) {
+                if (isInterestingFile(f)) return true;
+                if (f.isDirectory()) explore.add(f);
+            }
+        }
+        return false;
+    }
+
+    private static boolean isInterestingFile(File file) {
+        if (file.isFile()) {
+            final String name = file.getName().toLowerCase();
+            if (name.endsWith(".exe") || name.equals("autorun.inf")
+                    || name.equals("launchpad.zip") || name.equals(".nomedia")) {
+                return false;
+            } else {
+                return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Get a top-level shared/external storage directory for placing files of a
+     * particular type. This is where the user will typically place and manage
+     * their own files, so you should be careful about what you put here to
+     * ensure you don't erase their files or get in the way of their own
+     * organization.
+     * <p>
+     * On devices with multiple users (as described by {@link UserManager}),
+     * each user has their own isolated shared storage. Applications only have
+     * access to the shared storage for the user they're running as.
+     * </p>
+     * <p>
+     * Here is an example of typical code to manipulate a picture on the public
+     * shared storage:
+     * </p>
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * public_picture}
+     * <p>
+     * Note that alternatives such as {@link Context#getExternalFilesDir(String)} or
+     * {@link MediaStore} offer better performance.
+     *
+     * @param type The type of storage directory to return. Should be one of
+     *            {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
+     *            {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
+     *            {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
+     *            {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS},
+     *            {@link #DIRECTORY_DCIM}, or {@link #DIRECTORY_DOCUMENTS}. May not be null.
+     * @return Returns the File path for the directory. Note that this directory
+     *         may not yet exist, so you must make sure it exists before using
+     *         it such as with {@link File#mkdirs File.mkdirs()}.
+     */
+    public static File getExternalStoragePublicDirectory(String type) {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
+    }
+
+    /**
+     * Returns the path for android-specific data on the SD card.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static File[] buildExternalStorageAndroidDataDirs() {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStorageAndroidDataDirs();
+    }
+
+    /**
+     * Returns the path for android-specific OBB data on the SD card.
+     * @hide
+     */
+    public static File[] buildExternalStorageAndroidObbDirs() {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStorageAndroidObbDirs();
+    }
+
+    /**
+     * Generates the raw path to an application's data
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static File[] buildExternalStorageAppDataDirs(String packageName) {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStorageAppDataDirs(packageName);
+    }
+
+    /**
+     * Generates the raw path to an application's media
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static File[] buildExternalStorageAppMediaDirs(String packageName) {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStorageAppMediaDirs(packageName);
+    }
+
+    /**
+     * Generates the raw path to an application's OBB files
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static File[] buildExternalStorageAppObbDirs(String packageName) {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStorageAppObbDirs(packageName);
+    }
+
+    /**
+     * Generates the path to an application's files.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static File[] buildExternalStorageAppFilesDirs(String packageName) {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStorageAppFilesDirs(packageName);
+    }
+
+    /**
+     * Generates the path to an application's cache.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static File[] buildExternalStorageAppCacheDirs(String packageName) {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStorageAppCacheDirs(packageName);
+    }
+
+    /** @hide */
+    public static File[] buildExternalStoragePublicDirs(@NonNull String dirType) {
+        throwIfUserRequired();
+        return sCurrentUser.buildExternalStoragePublicDirs(dirType);
+    }
+
+    /**
+     * Return the download/cache content directory.
+     */
+    public static File getDownloadCacheDirectory() {
+        return DIR_DOWNLOAD_CACHE;
+    }
+
+    /**
+     * Return the metadata directory.
+     *
+     * @hide
+     */
+    public static @NonNull File getMetadataDirectory() {
+        return DIR_METADATA;
+    }
+
+    /**
+     * Unknown storage state, such as when a path isn't backed by known storage
+     * media.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_UNKNOWN = "unknown";
+
+    /**
+     * Storage state if the media is not present.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_REMOVED = "removed";
+
+    /**
+     * Storage state if the media is present but not mounted.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_UNMOUNTED = "unmounted";
+
+    /**
+     * Storage state if the media is present and being disk-checked.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_CHECKING = "checking";
+
+    /**
+     * Storage state if the media is present but is blank or is using an
+     * unsupported filesystem.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_NOFS = "nofs";
+
+    /**
+     * Storage state if the media is present and mounted at its mount point with
+     * read/write access.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_MOUNTED = "mounted";
+
+    /**
+     * Storage state if the media is present and mounted at its mount point with
+     * read-only access.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
+
+    /**
+     * Storage state if the media is present not mounted, and shared via USB
+     * mass storage.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_SHARED = "shared";
+
+    /**
+     * Storage state if the media was removed before it was unmounted.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_BAD_REMOVAL = "bad_removal";
+
+    /**
+     * Storage state if the media is present but cannot be mounted. Typically
+     * this happens if the file system on the media is corrupted.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_UNMOUNTABLE = "unmountable";
+
+    /**
+     * Storage state if the media is in the process of being ejected.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_EJECTING = "ejecting";
+
+    /**
+     * Returns the current state of the primary shared/external storage media.
+     *
+     * @see #getExternalStorageDirectory()
+     * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
+     *         {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
+     *         {@link #MEDIA_NOFS}, {@link #MEDIA_MOUNTED},
+     *         {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED},
+     *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
+     */
+    public static String getExternalStorageState() {
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
+        return getExternalStorageState(externalDir);
+    }
+
+    /**
+     * @deprecated use {@link #getExternalStorageState(File)}
+     */
+    @Deprecated
+    public static String getStorageState(File path) {
+        return getExternalStorageState(path);
+    }
+
+    /**
+     * Returns the current state of the shared/external storage media at the
+     * given path.
+     *
+     * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
+     *         {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
+     *         {@link #MEDIA_NOFS}, {@link #MEDIA_MOUNTED},
+     *         {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED},
+     *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
+     */
+    public static String getExternalStorageState(File path) {
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
+        if (volume != null) {
+            return volume.getState();
+        } else {
+            return MEDIA_UNKNOWN;
+        }
+    }
+
+    /**
+     * Returns whether the primary shared/external storage media is physically
+     * removable.
+     *
+     * @return true if the storage device can be removed (such as an SD card),
+     *         or false if the storage device is built in and cannot be
+     *         physically removed.
+     */
+    public static boolean isExternalStorageRemovable() {
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
+        return isExternalStorageRemovable(externalDir);
+    }
+
+    /**
+     * Returns whether the shared/external storage media at the given path is
+     * physically removable.
+     *
+     * @return true if the storage device can be removed (such as an SD card),
+     *         or false if the storage device is built in and cannot be
+     *         physically removed.
+     * @throws IllegalArgumentException if the path is not a valid storage
+     *             device.
+     */
+    public static boolean isExternalStorageRemovable(@NonNull File path) {
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
+        if (volume != null) {
+            return volume.isRemovable();
+        } else {
+            throw new IllegalArgumentException("Failed to find storage device at " + path);
+        }
+    }
+
+    /**
+     * Returns whether the primary shared/external storage media is emulated.
+     * <p>
+     * The contents of emulated storage devices are backed by a private user
+     * data partition, which means there is little benefit to apps storing data
+     * here instead of the private directories returned by
+     * {@link Context#getFilesDir()}, etc.
+     * <p>
+     * This returns true when emulated storage is backed by either internal
+     * storage or an adopted storage device.
+     *
+     * @see DevicePolicyManager#setStorageEncryption(android.content.ComponentName,
+     *      boolean)
+     */
+    public static boolean isExternalStorageEmulated() {
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
+        return isExternalStorageEmulated(externalDir);
+    }
+
+    /**
+     * Returns whether the shared/external storage media at the given path is
+     * emulated.
+     * <p>
+     * The contents of emulated storage devices are backed by a private user
+     * data partition, which means there is little benefit to apps storing data
+     * here instead of the private directories returned by
+     * {@link Context#getFilesDir()}, etc.
+     * <p>
+     * This returns true when emulated storage is backed by either internal
+     * storage or an adopted storage device.
+     *
+     * @throws IllegalArgumentException if the path is not a valid storage
+     *             device.
+     */
+    public static boolean isExternalStorageEmulated(@NonNull File path) {
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
+        if (volume != null) {
+            return volume.isEmulated();
+        } else {
+            throw new IllegalArgumentException("Failed to find storage device at " + path);
+        }
+    }
+
+    /**
+     * Returns whether the shared/external storage media is a
+     * legacy view that includes files not owned by the app.
+     * <p>
+     * This value may be different from the value requested by
+     * {@code requestLegacyExternalStorage} in the app's manifest, since an app
+     * may inherit its legacy state based on when it was first installed, target sdk and other
+     * factors.
+     * <p>
+     * Non-legacy apps can continue to discover and read media belonging to
+     * other apps via {@link android.provider.MediaStore}.
+     */
+    public static boolean isExternalStorageLegacy() {
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
+        return isExternalStorageLegacy(externalDir);
+    }
+
+    /**
+     * Returns whether the shared/external storage media is a
+     * legacy view that includes files not owned by the app.
+     * <p>
+     * This value may be different from the value requested by
+     * {@code requestLegacyExternalStorage} in the app's manifest, since an app
+     * may inherit its legacy state based on when it was first installed, target sdk and other
+     * factors.
+     * <p>
+     * Non-legacy apps can continue to discover and read media belonging to
+     * other apps via {@link android.provider.MediaStore}.
+     *
+     * @throws IllegalArgumentException if the path is not a valid storage
+     * device.
+     */
+    public static boolean isExternalStorageLegacy(@NonNull File path) {
+        final Context context = AppGlobals.getInitialApplication();
+        final int uid = context.getApplicationInfo().uid;
+        // Isolated processes and Instant apps are never allowed to be in scoped storage
+        if (Process.isIsolated(uid) || Process.isSdkSandboxUid(uid)) {
+            return false;
+        }
+
+        final PackageManager packageManager = context.getPackageManager();
+        if (packageManager.isInstantApp()) {
+            return false;
+        }
+
+        // Apps with PROPERTY_NO_APP_DATA_STORAGE should not be allowed in scoped storage
+        final String packageName = AppGlobals.getInitialPackage();
+        try {
+            final PackageManager.Property noAppStorageProp = packageManager.getProperty(
+                    PackageManager.PROPERTY_NO_APP_DATA_STORAGE, packageName);
+            if (noAppStorageProp != null && noAppStorageProp.getBoolean()) {
+                return false;
+            }
+        } catch (PackageManager.NameNotFoundException ignore) {
+            // Property not defined for the package
+        }
+
+        boolean defaultScopedStorage = Compatibility.isChangeEnabled(DEFAULT_SCOPED_STORAGE);
+        boolean forceEnableScopedStorage = Compatibility.isChangeEnabled(
+                FORCE_ENABLE_SCOPED_STORAGE);
+        // if Scoped Storage is strictly enforced, the app does *not* have legacy storage access
+        // Note: does not require packagename/uid as this is directly called from an app process
+        if (isScopedStorageEnforced(defaultScopedStorage, forceEnableScopedStorage)) {
+            return false;
+        }
+        // if Scoped Storage is strictly disabled, the app has legacy storage access
+        // Note: does not require packagename/uid as this is directly called from an app process
+        if (isScopedStorageDisabled(defaultScopedStorage, forceEnableScopedStorage)) {
+            return true;
+        }
+
+        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+        final String opPackageName = context.getOpPackageName();
+
+        if (appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid,
+                opPackageName) == AppOpsManager.MODE_ALLOWED) {
+            return true;
+        }
+
+        // Legacy external storage access is granted to instrumentations invoked with
+        // "--no-isolated-storage" flag.
+        return appOps.noteOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid,
+                opPackageName) == AppOpsManager.MODE_ALLOWED;
+    }
+
+    private static boolean isScopedStorageEnforced(boolean defaultScopedStorage,
+            boolean forceEnableScopedStorage) {
+        return defaultScopedStorage && forceEnableScopedStorage;
+    }
+
+    private static boolean isScopedStorageDisabled(boolean defaultScopedStorage,
+            boolean forceEnableScopedStorage) {
+        return !defaultScopedStorage && !forceEnableScopedStorage;
+    }
+
+    /**
+     * Returns whether the calling app has All Files Access on the primary shared/external storage
+     * media.
+     * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't
+     * enough to gain the access.
+     * <p>To request access, use
+     * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}.
+     */
+    public static boolean isExternalStorageManager() {
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
+        return isExternalStorageManager(externalDir);
+    }
+
+    /**
+     * Returns whether the calling app has All Files Access at the given {@code path}
+     * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't
+     * enough to gain the access.
+     * <p>To request access, use
+     * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}.
+     */
+    public static boolean isExternalStorageManager(@NonNull File path) {
+        final Context context = Objects.requireNonNull(AppGlobals.getInitialApplication());
+        String packageName = Objects.requireNonNull(context.getPackageName());
+        int uid = context.getApplicationInfo().uid;
+
+        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+        final int opMode =
+                appOps.checkOpNoThrow(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);
+
+        switch (opMode) {
+            case AppOpsManager.MODE_DEFAULT:
+                return PackageManager.PERMISSION_GRANTED
+                        == context.checkPermission(
+                                Manifest.permission.MANAGE_EXTERNAL_STORAGE, Process.myPid(), uid);
+            case AppOpsManager.MODE_ALLOWED:
+                return true;
+            case AppOpsManager.MODE_ERRORED:
+            case AppOpsManager.MODE_IGNORED:
+                return false;
+            default:
+                throw new IllegalStateException("Unknown AppOpsManager mode " + opMode);
+        }
+    }
+
+    static File getDirectory(String variableName, String defaultPath) {
+        String path = System.getenv(variableName);
+        return path == null ? new File(defaultPath) : new File(path);
+    }
+
+    @NonNull
+    static String getDirectoryPath(@NonNull String variableName, @NonNull String defaultPath) {
+        String path = System.getenv(variableName);
+        return path == null ? defaultPath : path;
+    }
+
+    /** {@hide} */
+    public static void setUserRequired(boolean userRequired) {
+        sUserRequired = userRequired;
+    }
+
+    private static void throwIfUserRequired() {
+        if (sUserRequired) {
+            Log.wtf(TAG, "Path requests must specify a user by using UserEnvironment",
+                    new Throwable());
+        }
+    }
+
+    /**
+     * Append path segments to each given base path, returning result.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static File[] buildPaths(File[] base, String... segments) {
+        File[] result = new File[base.length];
+        for (int i = 0; i < base.length; i++) {
+            result[i] = buildPath(base[i], segments);
+        }
+        return result;
+    }
+
+    /**
+     * Append path segments to given base path, returning result.
+     *
+     * @hide
+     */
+    @TestApi
+    public static File buildPath(File base, String... segments) {
+        File cur = base;
+        for (String segment : segments) {
+            if (cur == null) {
+                cur = new File(segment);
+            } else {
+                cur = new File(cur, segment);
+            }
+        }
+        return cur;
+    }
+
+    /**
+     * If the given path exists on emulated external storage, return the
+     * translated backing path hosted on internal storage. This bypasses any
+     * emulation later, improving performance. This is <em>only</em> suitable
+     * for read-only access.
+     * <p>
+     * Returns original path if given path doesn't meet these criteria. Callers
+     * must hold {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}
+     * permission.
+     *
+     * @deprecated disabled now that FUSE has been replaced by sdcardfs
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public static File maybeTranslateEmulatedPathToInternal(File path) {
+        return StorageManager.maybeTranslateEmulatedPathToInternal(path);
+    }
+}
diff --git a/android-34/android/os/ExternalVibration.java b/android-34/android/os/ExternalVibration.java
new file mode 100644
index 0000000..fb115b3
--- /dev/null
+++ b/android-34/android/os/ExternalVibration.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioAttributes;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * An ExternalVibration represents an on-going vibration being controlled by something other than
+ * the core vibrator service.
+ *
+ * @hide
+ */
+public class ExternalVibration implements Parcelable {
+    private static final String TAG = "ExternalVibration";
+    private int mUid;
+    @NonNull
+    private String mPkg;
+    @NonNull
+    private AudioAttributes mAttrs;
+    @NonNull
+    private IExternalVibrationController mController;
+    // A token used to maintain equality comparisons when passing objects across process
+    // boundaries.
+    @NonNull
+    private IBinder mToken;
+    public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
+            @NonNull IExternalVibrationController controller) {
+        this(uid, pkg, attrs, controller, new Binder());
+    }
+
+    /**
+     * Full constructor, but exposed to construct the ExternalVibration with an explicit binder
+     * token (for mocks).
+     *
+     * @hide
+     */
+    public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
+            @NonNull IExternalVibrationController controller, @NonNull IBinder token) {
+        mUid = uid;
+        mPkg = Preconditions.checkNotNull(pkg);
+        mAttrs = Preconditions.checkNotNull(attrs);
+        mController = Preconditions.checkNotNull(controller);
+        mToken = Preconditions.checkNotNull(token);
+
+        // IExternalVibrationController is a hidden AIDL interface with implementation provided by
+        // the audio framework to allow mute/unmute control over the external vibration.
+        //
+        // Transactions are locked in audioflinger, and should be blocking to avoid racing
+        // conditions on multiple audio playback.
+        //
+        // They can also be triggered before starting a new external vibration in
+        // IExternalVibratorService, as the ongoing external vibration needs to be muted before the
+        // new one can start, which also requires blocking calls to mute.
+        Binder.allowBlocking(mController.asBinder());
+    }
+
+    private ExternalVibration(Parcel in) {
+        this(in.readInt(), in.readString(), readAudioAttributes(in),
+                IExternalVibrationController.Stub.asInterface(in.readStrongBinder()),
+                in.readStrongBinder());
+    }
+
+    private static AudioAttributes readAudioAttributes(Parcel in) {
+        int usage = in.readInt();
+        int contentType = in.readInt();
+        int capturePreset = in.readInt();
+        int flags = in.readInt();
+        AudioAttributes.Builder builder = new AudioAttributes.Builder();
+        return builder.setUsage(usage)
+                .setContentType(contentType)
+                .setCapturePreset(capturePreset)
+                .setFlags(flags)
+                .build();
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public String getPackage() {
+        return mPkg;
+    }
+
+    public AudioAttributes getAudioAttributes() {
+        return mAttrs;
+    }
+
+    public IBinder getToken() {
+        return mToken;
+    }
+
+    public VibrationAttributes getVibrationAttributes() {
+        return new VibrationAttributes.Builder(mAttrs).build();
+    }
+
+    /**
+     * Mutes the external vibration if it's playing and unmuted.
+     *
+     * @return whether the muting operation was successful
+     */
+    public boolean mute() {
+        try {
+            mController.mute();
+        } catch (RemoteException e) {
+            Slog.wtf(TAG, "Failed to mute vibration stream: " + this, e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Unmutes the external vibration if it's playing and muted.
+     *
+     * @return whether the unmuting operation was successful
+     */
+    public boolean unmute() {
+        try {
+            mController.unmute();
+        } catch (RemoteException e) {
+            Slog.wtf(TAG, "Failed to unmute vibration stream: " + this, e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Links a recipient to death against this external vibration token
+     */
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        try {
+            mToken.linkToDeath(recipient, 0);
+        } catch (RemoteException e) {
+            return;
+        }
+    }
+
+    /**
+     * Unlinks a recipient to death against this external vibration token
+     */
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mToken.unlinkToDeath(recipient, 0);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof ExternalVibration)) {
+            return false;
+        }
+        ExternalVibration other = (ExternalVibration) o;
+        return mToken.equals(other.mToken);
+    }
+
+    @Override
+    public String toString() {
+        return "ExternalVibration{"
+            + "uid=" + mUid + ", "
+            + "pkg=" + mPkg + ", "
+            + "attrs=" + mAttrs + ", "
+            + "controller=" + mController
+            + "token=" + mToken
+            + "}";
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mUid);
+        out.writeString(mPkg);
+        writeAudioAttributes(mAttrs, out, flags);
+        out.writeStrongBinder(mController.asBinder());
+        out.writeStrongBinder(mToken);
+    }
+
+    private static void writeAudioAttributes(AudioAttributes attrs, Parcel out, int flags) {
+        out.writeInt(attrs.getUsage());
+        out.writeInt(attrs.getContentType());
+        out.writeInt(attrs.getCapturePreset());
+        out.writeInt(attrs.getAllFlags());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ExternalVibration> CREATOR =
+            new Parcelable.Creator<ExternalVibration>() {
+                @Override
+                public ExternalVibration createFromParcel(Parcel in) {
+                    return new ExternalVibration(in);
+                }
+
+                @Override
+                public ExternalVibration[] newArray(int size) {
+                    return new ExternalVibration[size];
+                }
+            };
+}
diff --git a/android-34/android/os/FactoryTest.java b/android-34/android/os/FactoryTest.java
new file mode 100644
index 0000000..b59227c
--- /dev/null
+++ b/android-34/android/os/FactoryTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import com.android.internal.os.RoSystemProperties;
+
+/**
+ * Provides support for in-place factory test functions.
+ *
+ * This class provides a few properties that alter the normal operation of the system
+ * during factory testing.
+ *
+ * {@hide}
+ */
+public final class FactoryTest {
+    public static final int FACTORY_TEST_OFF = 0;
+    public static final int FACTORY_TEST_LOW_LEVEL = 1;
+    public static final int FACTORY_TEST_HIGH_LEVEL = 2;
+
+    /**
+     * Gets the current factory test mode.
+     *
+     * @return One of: {@link #FACTORY_TEST_OFF}, {@link #FACTORY_TEST_LOW_LEVEL},
+     * or {@link #FACTORY_TEST_HIGH_LEVEL}.
+     */
+    public static int getMode() {
+        return RoSystemProperties.FACTORYTEST;
+    }
+
+    /**
+     * When true, long-press on power should immediately cause the device to
+     * shut down, without prompting the user.
+     */
+    public static boolean isLongPressOnPowerOffEnabled() {
+        return SystemProperties.getInt("factory.long_press_power_off", 0) != 0;
+    }
+}
diff --git a/android-34/android/os/FileBridge.java b/android-34/android/os/FileBridge.java
new file mode 100644
index 0000000..9dcdbf9
--- /dev/null
+++ b/android-34/android/os/FileBridge.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 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.os;
+
+import static android.system.OsConstants.SOCK_STREAM;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoBridge;
+import libcore.io.IoUtils;
+import libcore.io.Memory;
+import libcore.io.Streams;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Simple bridge that allows file access across process boundaries without
+ * returning the underlying {@link FileDescriptor}. This is useful when the
+ * server side needs to strongly assert that a client side is completely
+ * hands-off.
+ *
+ * @hide
+ * @deprecated replaced by {@link RevocableFileDescriptor}
+ */
+@Deprecated
+public class FileBridge extends Thread {
+    private static final String TAG = "FileBridge";
+
+    // TODO: consider extending to support bidirectional IO
+
+    private static final int MSG_LENGTH = 8;
+
+    /** CMD_WRITE [len] [data] */
+    private static final int CMD_WRITE = 1;
+    /** CMD_FSYNC */
+    private static final int CMD_FSYNC = 2;
+    /** CMD_CLOSE */
+    private static final int CMD_CLOSE = 3;
+
+    private ParcelFileDescriptor mTarget;
+
+    private ParcelFileDescriptor mServer;
+    private ParcelFileDescriptor mClient;
+
+    private volatile boolean mClosed;
+
+    public FileBridge() {
+        try {
+            ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair(SOCK_STREAM);
+            mServer = fds[0];
+            mClient = fds[1];
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to create bridge");
+        }
+    }
+
+    public boolean isClosed() {
+        return mClosed;
+    }
+
+    public void forceClose() {
+        IoUtils.closeQuietly(mTarget);
+        IoUtils.closeQuietly(mServer);
+        mClosed = true;
+    }
+
+    public void setTargetFile(ParcelFileDescriptor target) {
+        mTarget = target;
+    }
+
+    public ParcelFileDescriptor getClientSocket() {
+        return mClient;
+    }
+
+    @Override
+    public void run() {
+        final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(8192);
+        final byte[] temp = tempBuffer.hasArray() ? tempBuffer.array() : new byte[8192];
+        try {
+            while (IoBridge.read(mServer.getFileDescriptor(), temp,
+                                 0, MSG_LENGTH) == MSG_LENGTH) {
+                final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
+                if (cmd == CMD_WRITE) {
+                    // Shuttle data into local file
+                    int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
+                    while (len > 0) {
+                        int n = IoBridge.read(mServer.getFileDescriptor(), temp, 0,
+                                              Math.min(temp.length, len));
+                        if (n == -1) {
+                            throw new IOException(
+                                    "Unexpected EOF; still expected " + len + " bytes");
+                        }
+                        IoBridge.write(mTarget.getFileDescriptor(), temp, 0, n);
+                        len -= n;
+                    }
+
+                } else if (cmd == CMD_FSYNC) {
+                    // Sync and echo back to confirm
+                    Os.fsync(mTarget.getFileDescriptor());
+                    IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH);
+
+                } else if (cmd == CMD_CLOSE) {
+                    // Close and echo back to confirm
+                    Os.fsync(mTarget.getFileDescriptor());
+                    mTarget.close();
+                    mClosed = true;
+                    IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH);
+                    break;
+                }
+            }
+
+        } catch (ErrnoException | IOException e) {
+            Log.wtf(TAG, "Failed during bridge", e);
+        } finally {
+            forceClose();
+        }
+    }
+
+    public static class FileBridgeOutputStream extends OutputStream {
+        private final ParcelFileDescriptor mClientPfd;
+        private final FileDescriptor mClient;
+        private final ByteBuffer mTempBuffer = ByteBuffer.allocateDirect(MSG_LENGTH);
+        private final byte[] mTemp = mTempBuffer.hasArray()
+                                     ? mTempBuffer.array()
+                                     : new byte[MSG_LENGTH];
+
+        public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
+            mClientPfd = clientPfd;
+            mClient = clientPfd.getFileDescriptor();
+        }
+
+        @Override
+        public void close() throws IOException {
+            try {
+                writeCommandAndBlock(CMD_CLOSE, "close()");
+            } finally {
+                IoUtils.closeQuietly(mClientPfd);
+            }
+        }
+
+        public void fsync() throws IOException {
+            writeCommandAndBlock(CMD_FSYNC, "fsync()");
+        }
+
+        private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
+            Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
+            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+
+            // Wait for server to ack
+            if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
+                if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
+                    return;
+                }
+            }
+
+            throw new IOException("Failed to execute " + cmdString + " across bridge");
+        }
+
+        @Override
+        public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+            ArrayUtils.throwsIfOutOfBounds(buffer.length, byteOffset, byteCount);
+            Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
+            Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
+            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+            IoBridge.write(mClient, buffer, byteOffset, byteCount);
+        }
+
+        @Override
+        public void write(int oneByte) throws IOException {
+            Streams.writeSingleByte(this, oneByte);
+        }
+    }
+}
diff --git a/android-34/android/os/FileObserver.java b/android-34/android/os/FileObserver.java
new file mode 100644
index 0000000..6f44b20
--- /dev/null
+++ b/android-34/android/os/FileObserver.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>)
+ * to fire an event after files are accessed or changed by any process on
+ * the device (including this one).  FileObserver is an abstract class;
+ * subclasses must implement the event handler {@link #onEvent(int, String)}.
+ *
+ * <p>Each FileObserver instance can monitor multiple files or directories.
+ * If a directory is monitored, events will be triggered for all files and
+ * subdirectories inside the monitored directory.</p>
+ *
+ * <p>An event mask is used to specify which changes or actions to report.
+ * Event type constants are used to describe the possible changes in the
+ * event mask as well as what actually happened in event callbacks.</p>
+ *
+ * <p class="caution"><b>Warning</b>: If a FileObserver is garbage collected, it
+ * will stop sending events.  To ensure you keep receiving events, you must
+ * keep a reference to the FileObserver instance from some other live object.</p>
+ */
+public abstract class FileObserver {
+    /** @hide */
+    @IntDef(flag = true, value = {
+            ACCESS,
+            MODIFY,
+            ATTRIB,
+            CLOSE_WRITE,
+            CLOSE_NOWRITE,
+            OPEN,
+            MOVED_FROM,
+            MOVED_TO,
+            CREATE,
+            DELETE,
+            DELETE_SELF,
+            MOVE_SELF
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NotifyEventType {}
+
+    /** Event type: Data was read from a file */
+    public static final int ACCESS = 0x00000001;
+    /** Event type: Data was written to a file */
+    public static final int MODIFY = 0x00000002;
+    /** Event type: Metadata (permissions, owner, timestamp) was changed explicitly */
+    public static final int ATTRIB = 0x00000004;
+    /** Event type: Someone had a file or directory open for writing, and closed it */
+    public static final int CLOSE_WRITE = 0x00000008;
+    /** Event type: Someone had a file or directory open read-only, and closed it */
+    public static final int CLOSE_NOWRITE = 0x00000010;
+    /** Event type: A file or directory was opened */
+    public static final int OPEN = 0x00000020;
+    /** Event type: A file or subdirectory was moved from the monitored directory */
+    public static final int MOVED_FROM = 0x00000040;
+    /** Event type: A file or subdirectory was moved to the monitored directory */
+    public static final int MOVED_TO = 0x00000080;
+    /** Event type: A new file or subdirectory was created under the monitored directory */
+    public static final int CREATE = 0x00000100;
+    /** Event type: A file was deleted from the monitored directory */
+    public static final int DELETE = 0x00000200;
+    /** Event type: The monitored file or directory was deleted; monitoring effectively stops */
+    public static final int DELETE_SELF = 0x00000400;
+    /** Event type: The monitored file or directory was moved; monitoring continues */
+    public static final int MOVE_SELF = 0x00000800;
+
+    /** Event mask: All valid event types, combined */
+    @NotifyEventType
+    public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
+            | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
+            | DELETE_SELF | MOVE_SELF;
+
+    private static final String LOG_TAG = "FileObserver";
+
+    private static class ObserverThread extends Thread {
+        /** Temporarily retained; appears to be missing UnsupportedAppUsage annotation */
+        private HashMap<Integer, WeakReference> m_observers = new HashMap<Integer, WeakReference>();
+        private SparseArray<WeakReference> mRealObservers = new SparseArray<>();
+        private int m_fd;
+
+        public ObserverThread() {
+            super("FileObserver");
+            m_fd = init();
+        }
+
+        public void run() {
+            observe(m_fd);
+        }
+
+        public int[] startWatching(List<File> files,
+                @NotifyEventType int mask, FileObserver observer) {
+            final int count = files.size();
+            final String[] paths = new String[count];
+            for (int i = 0; i < count; ++i) {
+                paths[i] = files.get(i).getAbsolutePath();
+            }
+            final int[] wfds = new int[count];
+            Arrays.fill(wfds, -1);
+
+            startWatching(m_fd, paths, mask, wfds);
+
+            final WeakReference<FileObserver> fileObserverWeakReference =
+                    new WeakReference<>(observer);
+            synchronized (mRealObservers) {
+                for (int wfd : wfds) {
+                    if (wfd >= 0) {
+                        mRealObservers.put(wfd, fileObserverWeakReference);
+                    }
+                }
+            }
+
+            return wfds;
+        }
+
+        public void stopWatching(int[] descriptors) {
+            stopWatching(m_fd, descriptors);
+        }
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public void onEvent(int wfd, @NotifyEventType int mask, String path) {
+            // look up our observer, fixing up the map if necessary...
+            FileObserver observer = null;
+
+            synchronized (mRealObservers) {
+                WeakReference weak = mRealObservers.get(wfd);
+                if (weak != null) {  // can happen with lots of events from a dead wfd
+                    observer = (FileObserver) weak.get();
+                    if (observer == null) {
+                        mRealObservers.remove(wfd);
+                    }
+                }
+            }
+
+            // ...then call out to the observer without the sync lock held
+            if (observer != null) {
+                try {
+                    observer.onEvent(mask, path);
+                } catch (Throwable throwable) {
+                    Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable);
+                }
+            }
+        }
+
+        private native int init();
+        private native void observe(int fd);
+        private native void startWatching(int fd, String[] paths,
+                @NotifyEventType int mask, int[] wfds);
+        private native void stopWatching(int fd, int[] wfds);
+    }
+
+    @UnsupportedAppUsage
+    private static ObserverThread s_observerThread;
+
+    static {
+        s_observerThread = new ObserverThread();
+        s_observerThread.start();
+    }
+
+    // instance
+    private final List<File> mFiles;
+    private int[] mDescriptors;
+    private final int mMask;
+
+    /**
+     * Equivalent to FileObserver(path, FileObserver.ALL_EVENTS).
+     *
+     * @deprecated use {@link #FileObserver(File)} instead.
+     */
+    @Deprecated
+    public FileObserver(String path) {
+        this(new File(path));
+    }
+
+    /**
+     * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS).
+     */
+    public FileObserver(@NonNull File file) {
+        this(Arrays.asList(file));
+    }
+
+    /**
+     * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS).
+     *
+     * @param files The files or directories to monitor
+     */
+    public FileObserver(@NonNull List<File> files) {
+        this(files, ALL_EVENTS);
+    }
+
+    /**
+     * Create a new file observer for a certain file or directory.
+     * Monitoring does not start on creation!  You must call
+     * {@link #startWatching()} before you will receive events.
+     *
+     * @param path The file or directory to monitor
+     * @param mask The event or events (added together) to watch for
+     *
+     * @deprecated use {@link #FileObserver(File, int)} instead.
+     */
+    @Deprecated
+    public FileObserver(String path, @NotifyEventType int mask) {
+        this(new File(path), mask);
+    }
+
+    /**
+     * Create a new file observer for a certain file or directory.
+     * Monitoring does not start on creation!  You must call
+     * {@link #startWatching()} before you will receive events.
+     *
+     * @param file The file or directory to monitor
+     * @param mask The event or events (added together) to watch for
+     */
+    public FileObserver(@NonNull File file, @NotifyEventType int mask) {
+        this(Arrays.asList(file), mask);
+    }
+
+    /**
+     * Version of {@link #FileObserver(File, int)} that allows callers to monitor
+     * multiple files or directories.
+     *
+     * @param files The files or directories to monitor
+     * @param mask The event or events (added together) to watch for
+     */
+    public FileObserver(@NonNull List<File> files, @NotifyEventType int mask) {
+        mFiles = files;
+        mMask = mask;
+    }
+
+    protected void finalize() {
+        stopWatching();
+    }
+
+    /**
+     * Start watching for events.  The monitored file or directory must exist at
+     * this time, or else no events will be reported (even if it appears later).
+     * If monitoring is already started, this call has no effect.
+     */
+    public void startWatching() {
+        if (mDescriptors == null) {
+            mDescriptors = s_observerThread.startWatching(mFiles, mMask, this);
+        }
+    }
+
+    /**
+     * Stop watching for events.  Some events may be in process, so events
+     * may continue to be reported even after this method completes.  If
+     * monitoring is already stopped, this call has no effect.
+     */
+    public void stopWatching() {
+        if (mDescriptors != null) {
+            s_observerThread.stopWatching(mDescriptors);
+            mDescriptors = null;
+        }
+    }
+
+    /**
+     * The event handler, which must be implemented by subclasses.
+     *
+     * <p class="note">This method is invoked on a special FileObserver thread.
+     * It runs independently of any threads, so take care to use appropriate
+     * synchronization!  Consider using {@link Handler#post(Runnable)} to shift
+     * event handling work to the main thread to avoid concurrency problems.</p>
+     *
+     * <p>Event handlers must not throw exceptions.</p>
+     *
+     * @param event The type of event which happened
+     * @param path The path, relative to the main monitored file or directory,
+     *     of the file or directory which triggered the event.  This value can
+     *     be {@code null} for certain events, such as {@link #MOVE_SELF}.
+     */
+    public abstract void onEvent(int event, @Nullable String path);
+}
diff --git a/android-34/android/os/FileUriExposedException.java b/android-34/android/os/FileUriExposedException.java
new file mode 100644
index 0000000..a3af24d
--- /dev/null
+++ b/android-34/android/os/FileUriExposedException.java
@@ -0,0 +1,45 @@
+/*
+ * 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.os;
+
+import android.content.Intent;
+
+/**
+ * The exception that is thrown when an application exposes a {@code file://}
+ * {@link android.net.Uri} to another app.
+ * <p>
+ * This exposure is discouraged since the receiving app may not have access to
+ * the shared path. For example, the receiving app may not have requested the
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission,
+ * or the platform may be sharing the {@link android.net.Uri} across user
+ * profile boundaries.
+ * <p>
+ * Instead, apps should use {@code content://} Uris so the platform can extend
+ * temporary permission for the receiving app to access the resource.
+ * <p>
+ * This is only thrown for applications targeting {@link Build.VERSION_CODES#N}
+ * or higher. Applications targeting earlier SDK versions are allowed to share
+ * {@code file://} {@link android.net.Uri}, but it's strongly discouraged.
+ *
+ * @see androidx.core.content.FileProvider
+ * @see Intent#FLAG_GRANT_READ_URI_PERMISSION
+ */
+public class FileUriExposedException extends RuntimeException {
+    public FileUriExposedException(String message) {
+        super(message);
+    }
+}
diff --git a/android-34/android/os/FileUtils.java b/android-34/android/os/FileUtils.java
new file mode 100644
index 0000000..af09a06
--- /dev/null
+++ b/android-34/android/os/FileUtils.java
@@ -0,0 +1,1653 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import static android.os.ParcelFileDescriptor.MODE_APPEND;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.ENOSYS;
+import static android.system.OsConstants.F_OK;
+import static android.system.OsConstants.O_ACCMODE;
+import static android.system.OsConstants.O_APPEND;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDONLY;
+import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_TRUNC;
+import static android.system.OsConstants.O_WRONLY;
+import static android.system.OsConstants.R_OK;
+import static android.system.OsConstants.SPLICE_F_MORE;
+import static android.system.OsConstants.SPLICE_F_MOVE;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.W_OK;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.provider.DocumentsContract.Document;
+import android.provider.MediaStore;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
+import android.text.TextUtils;
+import android.util.DataUnit;
+import android.util.Log;
+import android.util.Slog;
+import android.webkit.MimeTypeMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.SizedInputStream;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
+
+/**
+ * Utility methods useful for working with files.
+ */
+public final class FileUtils {
+    private static final String TAG = "FileUtils";
+
+    /** {@hide} */ public static final int S_IRWXU = 00700;
+    /** {@hide} */ public static final int S_IRUSR = 00400;
+    /** {@hide} */ public static final int S_IWUSR = 00200;
+    /** {@hide} */ public static final int S_IXUSR = 00100;
+
+    /** {@hide} */ public static final int S_IRWXG = 00070;
+    /** {@hide} */ public static final int S_IRGRP = 00040;
+    /** {@hide} */ public static final int S_IWGRP = 00020;
+    /** {@hide} */ public static final int S_IXGRP = 00010;
+
+    /** {@hide} */ public static final int S_IRWXO = 00007;
+    /** {@hide} */ public static final int S_IROTH = 00004;
+    /** {@hide} */ public static final int S_IWOTH = 00002;
+    /** {@hide} */ public static final int S_IXOTH = 00001;
+
+    @UnsupportedAppUsage
+    private FileUtils() {
+    }
+
+    private static final String CAMERA_DIR_LOWER_CASE = "/storage/emulated/" + UserHandle.myUserId()
+            + "/dcim/camera";
+
+    /** Regular expression for safe filenames: no spaces or metacharacters.
+      *
+      * Use a preload holder so that FileUtils can be compile-time initialized.
+      */
+    private static class NoImagePreloadHolder {
+        public static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
+    }
+
+    // non-final so it can be toggled by Robolectric's ShadowFileUtils
+    private static boolean sEnableCopyOptimizations = true;
+    private static volatile int sMediaProviderAppId = -1;
+
+    private static final long COPY_CHECKPOINT_BYTES = 524288;
+
+    /**
+     * Listener that is called periodically as progress is made.
+     */
+    public interface ProgressListener {
+        public void onProgress(long progress);
+    }
+
+    /**
+     * Set owner and mode of of given {@link File}.
+     *
+     * @param mode to apply through {@code chmod}
+     * @param uid to apply through {@code chown}, or -1 to leave unchanged
+     * @param gid to apply through {@code chown}, or -1 to leave unchanged
+     * @return 0 on success, otherwise errno.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static int setPermissions(File path, int mode, int uid, int gid) {
+        return setPermissions(path.getAbsolutePath(), mode, uid, gid);
+    }
+
+    /**
+     * Set owner and mode of of given path.
+     *
+     * @param mode to apply through {@code chmod}
+     * @param uid to apply through {@code chown}, or -1 to leave unchanged
+     * @param gid to apply through {@code chown}, or -1 to leave unchanged
+     * @return 0 on success, otherwise errno.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static int setPermissions(String path, int mode, int uid, int gid) {
+        try {
+            Os.chmod(path, mode);
+        } catch (ErrnoException e) {
+            Slog.w(TAG, "Failed to chmod(" + path + "): " + e);
+            return e.errno;
+        }
+
+        if (uid >= 0 || gid >= 0) {
+            try {
+                Os.chown(path, uid, gid);
+            } catch (ErrnoException e) {
+                Slog.w(TAG, "Failed to chown(" + path + "): " + e);
+                return e.errno;
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Set owner and mode of of given {@link FileDescriptor}.
+     *
+     * @param mode to apply through {@code chmod}
+     * @param uid to apply through {@code chown}, or -1 to leave unchanged
+     * @param gid to apply through {@code chown}, or -1 to leave unchanged
+     * @return 0 on success, otherwise errno.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
+        try {
+            Os.fchmod(fd, mode);
+        } catch (ErrnoException e) {
+            Slog.w(TAG, "Failed to fchmod(): " + e);
+            return e.errno;
+        }
+
+        if (uid >= 0 || gid >= 0) {
+            try {
+                Os.fchown(fd, uid, gid);
+            } catch (ErrnoException e) {
+                Slog.w(TAG, "Failed to fchown(): " + e);
+                return e.errno;
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Copy the owner UID, owner GID, and mode bits from one file to another.
+     *
+     * @param from File where attributes should be copied from.
+     * @param to File where attributes should be copied to.
+     * @hide
+     */
+    public static void copyPermissions(@NonNull File from, @NonNull File to) throws IOException {
+        try {
+            final StructStat stat = Os.stat(from.getAbsolutePath());
+            Os.chmod(to.getAbsolutePath(), stat.st_mode);
+            Os.chown(to.getAbsolutePath(), stat.st_uid, stat.st_gid);
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * @deprecated use {@link Os#stat(String)} instead.
+     * @hide
+     */
+    @Deprecated
+    public static int getUid(String path) {
+        try {
+            return Os.stat(path).st_uid;
+        } catch (ErrnoException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Perform an fsync on the given FileOutputStream.  The stream at this
+     * point must be flushed but not yet closed.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean sync(FileOutputStream stream) {
+        try {
+            if (stream != null) {
+                stream.getFD().sync();
+            }
+            return true;
+        } catch (IOException e) {
+        }
+        return false;
+    }
+
+    /**
+     * @deprecated use {@link #copy(File, File)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public static boolean copyFile(File srcFile, File destFile) {
+        try {
+            copyFileOrThrow(srcFile, destFile);
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /**
+     * @deprecated use {@link #copy(File, File)} instead.
+     * @hide
+     */
+    @Deprecated
+    public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
+        try (InputStream in = new FileInputStream(srcFile)) {
+            copyToFileOrThrow(in, destFile);
+        }
+    }
+
+    /**
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public static boolean copyToFile(InputStream inputStream, File destFile) {
+        try {
+            copyToFileOrThrow(inputStream, destFile);
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /**
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     * @hide
+     */
+    @Deprecated
+    public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
+        if (destFile.exists()) {
+            destFile.delete();
+        }
+        try (FileOutputStream out = new FileOutputStream(destFile)) {
+            copy(in, out);
+            try {
+                Os.fsync(out.getFD());
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        }
+    }
+
+    /**
+     * Copy the contents of one file to another, replacing any existing content.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @return number of bytes copied.
+     * @hide
+     */
+    public static long copy(@NonNull File from, @NonNull File to) throws IOException {
+        return copy(from, to, null, null, null);
+    }
+
+    /**
+     * Copy the contents of one file to another, replacing any existing content.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
+     * @return number of bytes copied.
+     * @hide
+     */
+    public static long copy(@NonNull File from, @NonNull File to,
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
+        try (FileInputStream in = new FileInputStream(from);
+                FileOutputStream out = new FileOutputStream(to)) {
+            return copy(in, out, signal, executor, listener);
+        }
+    }
+
+    /**
+     * Copy the contents of one stream to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
+        return copy(in, out, null, null, null);
+    }
+
+    /**
+     * Copy the contents of one stream to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
+        if (sEnableCopyOptimizations) {
+            if (in instanceof FileInputStream && out instanceof FileOutputStream) {
+                return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
+                        signal, executor, listener);
+            }
+        }
+
+        // Worse case fallback to userspace
+        return copyInternalUserspace(in, out, signal, executor, listener);
+    }
+
+    /**
+     * Copy the contents of one FD to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
+            throws IOException {
+        return copy(in, out, null, null, null);
+    }
+
+    /**
+     * Copy the contents of one FD to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
+        return copy(in, out, Long.MAX_VALUE, signal, executor, listener);
+    }
+
+    /**
+     * Copy the contents of one FD to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param count the number of bytes to copy.
+     * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
+     * @return number of bytes copied.
+     * @hide
+     */
+    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out, long count,
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
+        if (sEnableCopyOptimizations) {
+            try {
+                final StructStat st_in = Os.fstat(in);
+                final StructStat st_out = Os.fstat(out);
+                if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
+                    try {
+                        return copyInternalSendfile(in, out, count, signal, executor, listener);
+                    } catch (ErrnoException e) {
+                        if (e.errno == EINVAL || e.errno == ENOSYS) {
+                            // sendfile(2) will fail in at least any of the following conditions:
+                            // 1. |in| doesn't support mmap(2)
+                            // 2. |out| was opened with O_APPEND
+                            // We fallback to userspace copy if that fails
+                            return copyInternalUserspace(in, out, count, signal, executor,
+                                    listener);
+                        }
+                        throw e;
+                    }
+                } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
+                    return copyInternalSplice(in, out, count, signal, executor, listener);
+                }
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        }
+
+        // Worse case fallback to userspace
+        return copyInternalUserspace(in, out, count, signal, executor, listener);
+    }
+
+    /**
+     * Requires one of input or output to be a pipe.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static long copyInternalSplice(FileDescriptor in, FileDescriptor out, long count,
+            CancellationSignal signal, Executor executor, ProgressListener listener)
+            throws ErrnoException {
+        long progress = 0;
+        long checkpoint = 0;
+
+        long t;
+        while ((t = Os.splice(in, null, out, null, Math.min(count, COPY_CHECKPOINT_BYTES),
+                SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
+            progress += t;
+            checkpoint += t;
+            count -= t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (executor != null && listener != null) {
+                    final long progressSnapshot = progress;
+                    executor.execute(() -> {
+                        listener.onProgress(progressSnapshot);
+                    });
+                }
+                checkpoint = 0;
+            }
+        }
+        if (executor != null && listener != null) {
+            final long progressSnapshot = progress;
+            executor.execute(() -> {
+                listener.onProgress(progressSnapshot);
+            });
+        }
+        return progress;
+    }
+
+    /**
+     * Requires both input and output to be a regular file.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out, long count,
+            CancellationSignal signal, Executor executor, ProgressListener listener)
+            throws ErrnoException {
+        long progress = 0;
+        long checkpoint = 0;
+
+        long t;
+        while ((t = Os.sendfile(out, in, null, Math.min(count, COPY_CHECKPOINT_BYTES))) != 0) {
+            progress += t;
+            checkpoint += t;
+            count -= t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (executor != null && listener != null) {
+                    final long progressSnapshot = progress;
+                    executor.execute(() -> {
+                        listener.onProgress(progressSnapshot);
+                    });
+                }
+                checkpoint = 0;
+            }
+        }
+        if (executor != null && listener != null) {
+            final long progressSnapshot = progress;
+            executor.execute(() -> {
+                listener.onProgress(progressSnapshot);
+            });
+        }
+        return progress;
+    }
+
+    /** {@hide} */
+    @Deprecated
+    @VisibleForTesting
+    public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
+            ProgressListener listener, CancellationSignal signal, long count)
+            throws IOException {
+        return copyInternalUserspace(in, out, count, signal, Runnable::run, listener);
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out, long count,
+            CancellationSignal signal, Executor executor, ProgressListener listener)
+            throws IOException {
+        if (count != Long.MAX_VALUE) {
+            return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
+                    new FileOutputStream(out), signal, executor, listener);
+        } else {
+            return copyInternalUserspace(new FileInputStream(in),
+                    new FileOutputStream(out), signal, executor, listener);
+        }
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static long copyInternalUserspace(InputStream in, OutputStream out,
+            CancellationSignal signal, Executor executor, ProgressListener listener)
+            throws IOException {
+        long progress = 0;
+        long checkpoint = 0;
+        byte[] buffer = new byte[8192];
+
+        int t;
+        while ((t = in.read(buffer)) != -1) {
+            out.write(buffer, 0, t);
+
+            progress += t;
+            checkpoint += t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (executor != null && listener != null) {
+                    final long progressSnapshot = progress;
+                    executor.execute(() -> {
+                        listener.onProgress(progressSnapshot);
+                    });
+                }
+                checkpoint = 0;
+            }
+        }
+        if (executor != null && listener != null) {
+            final long progressSnapshot = progress;
+            executor.execute(() -> {
+                listener.onProgress(progressSnapshot);
+            });
+        }
+        return progress;
+    }
+
+    /**
+     * Check if a filename is "safe" (no metacharacters or spaces).
+     * @param file  The file to check
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean isFilenameSafe(File file) {
+        // Note, we check whether it matches what's known to be safe,
+        // rather than what's known to be unsafe.  Non-ASCII, control
+        // characters, etc. are all unsafe by default.
+        return NoImagePreloadHolder.SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
+    }
+
+    /**
+     * Read a text file into a String, optionally limiting the length.
+     * @param file to read (will not seek, so things like /proc files are OK)
+     * @param max length (positive for head, negative of tail, 0 for no limit)
+     * @param ellipsis to add of the file was truncated (can be null)
+     * @return the contents of the file, possibly truncated
+     * @throws IOException if something goes wrong reading the file
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String readTextFile(File file, int max, String ellipsis) throws IOException {
+        InputStream input = new FileInputStream(file);
+        // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
+        // input stream, bytes read not equal to buffer size is not necessarily the correct
+        // indication for EOF; but it is true for BufferedInputStream due to its implementation.
+        BufferedInputStream bis = new BufferedInputStream(input);
+        try {
+            long size = file.length();
+            if (max > 0 || (size > 0 && max == 0)) {  // "head" mode: read the first N bytes
+                if (size > 0 && (max == 0 || size < max)) max = (int) size;
+                byte[] data = new byte[max + 1];
+                int length = bis.read(data);
+                if (length <= 0) return "";
+                if (length <= max) return new String(data, 0, length);
+                if (ellipsis == null) return new String(data, 0, max);
+                return new String(data, 0, max) + ellipsis;
+            } else if (max < 0) {  // "tail" mode: keep the last N
+                int len;
+                boolean rolled = false;
+                byte[] last = null;
+                byte[] data = null;
+                do {
+                    if (last != null) rolled = true;
+                    byte[] tmp = last; last = data; data = tmp;
+                    if (data == null) data = new byte[-max];
+                    len = bis.read(data);
+                } while (len == data.length);
+
+                if (last == null && len <= 0) return "";
+                if (last == null) return new String(data, 0, len);
+                if (len > 0) {
+                    rolled = true;
+                    System.arraycopy(last, len, last, 0, last.length - len);
+                    System.arraycopy(data, 0, last, last.length - len, len);
+                }
+                if (ellipsis == null || !rolled) return new String(last);
+                return ellipsis + new String(last);
+            } else {  // "cat" mode: size unknown, read it all in streaming fashion
+                ByteArrayOutputStream contents = new ByteArrayOutputStream();
+                int len;
+                byte[] data = new byte[1024];
+                do {
+                    len = bis.read(data);
+                    if (len > 0) contents.write(data, 0, len);
+                } while (len == data.length);
+                return contents.toString();
+            }
+        } finally {
+            bis.close();
+            input.close();
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static void stringToFile(File file, String string) throws IOException {
+        stringToFile(file.getAbsolutePath(), string);
+    }
+
+    /**
+     * Writes the bytes given in {@code content} to the file whose absolute path
+     * is {@code filename}.
+     *
+     * @hide
+     */
+    public static void bytesToFile(String filename, byte[] content) throws IOException {
+        if (filename.startsWith("/proc/")) {
+            final int oldMask = StrictMode.allowThreadDiskWritesMask();
+            try (FileOutputStream fos = new FileOutputStream(filename)) {
+                fos.write(content);
+            } finally {
+                StrictMode.setThreadPolicyMask(oldMask);
+            }
+        } else {
+            try (FileOutputStream fos = new FileOutputStream(filename)) {
+                fos.write(content);
+            }
+        }
+    }
+
+    /**
+     * Writes string to file. Basically same as "echo -n $string > $filename"
+     *
+     * @param filename
+     * @param string
+     * @throws IOException
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void stringToFile(String filename, String string) throws IOException {
+        bytesToFile(filename, string.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * Computes the checksum of a file using the CRC32 checksum routine. The
+     * value of the checksum is returned.
+     *
+     * @param file the file to checksum, must not be null
+     * @return the checksum value or an exception is thrown.
+     * @deprecated this is a weak hashing algorithm, and should not be used due
+     *             to its potential for collision.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @Deprecated
+    public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
+        CRC32 checkSummer = new CRC32();
+        CheckedInputStream cis = null;
+
+        try {
+            cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
+            byte[] buf = new byte[128];
+            while(cis.read(buf) >= 0) {
+                // Just read for checksum to get calculated.
+            }
+            return checkSummer.getValue();
+        } finally {
+            if (cis != null) {
+                try {
+                    cis.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Compute the digest of the given file using the requested algorithm.
+     *
+     * @param algorithm Any valid algorithm accepted by
+     *            {@link MessageDigest#getInstance(String)}.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static byte[] digest(@NonNull File file, @NonNull String algorithm)
+            throws IOException, NoSuchAlgorithmException {
+        try (FileInputStream in = new FileInputStream(file)) {
+            return digest(in, algorithm);
+        }
+    }
+
+    /**
+     * Compute the digest of the given file using the requested algorithm.
+     *
+     * @param algorithm Any valid algorithm accepted by
+     *            {@link MessageDigest#getInstance(String)}.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static byte[] digest(@NonNull InputStream in, @NonNull String algorithm)
+            throws IOException, NoSuchAlgorithmException {
+        // TODO: implement kernel optimizations
+        return digestInternalUserspace(in, algorithm);
+    }
+
+    /**
+     * Compute the digest of the given file using the requested algorithm.
+     *
+     * @param algorithm Any valid algorithm accepted by
+     *            {@link MessageDigest#getInstance(String)}.
+     * @hide
+     */
+    public static byte[] digest(FileDescriptor fd, String algorithm)
+            throws IOException, NoSuchAlgorithmException {
+        // TODO: implement kernel optimizations
+        return digestInternalUserspace(new FileInputStream(fd), algorithm);
+    }
+
+    private static byte[] digestInternalUserspace(InputStream in, String algorithm)
+            throws IOException, NoSuchAlgorithmException {
+        final MessageDigest digest = MessageDigest.getInstance(algorithm);
+        try (DigestInputStream digestStream = new DigestInputStream(in, digest)) {
+            final byte[] buffer = new byte[8192];
+            while (digestStream.read(buffer) != -1) {
+            }
+        }
+        return digest.digest();
+    }
+
+    /**
+     * Delete older files in a directory until only those matching the given
+     * constraints remain.
+     *
+     * @param minCount Always keep at least this many files.
+     * @param minAgeMs Always keep files younger than this age, in milliseconds.
+     * @return if any files were deleted.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static boolean deleteOlderFiles(File dir, int minCount, long minAgeMs) {
+        if (minCount < 0 || minAgeMs < 0) {
+            throw new IllegalArgumentException("Constraints must be positive or 0");
+        }
+
+        final File[] files = dir.listFiles();
+        if (files == null) return false;
+
+        // Sort with newest files first
+        Arrays.sort(files, new Comparator<File>() {
+            @Override
+            public int compare(File lhs, File rhs) {
+                return Long.compare(rhs.lastModified(), lhs.lastModified());
+            }
+        });
+
+        // Keep at least minCount files
+        boolean deleted = false;
+        for (int i = minCount; i < files.length; i++) {
+            final File file = files[i];
+
+            // Keep files newer than minAgeMs
+            final long age = System.currentTimeMillis() - file.lastModified();
+            if (age > minAgeMs) {
+                if (file.delete()) {
+                    Log.d(TAG, "Deleted old file " + file);
+                    deleted = true;
+                }
+            }
+        }
+        return deleted;
+    }
+
+    /**
+     * Test if a file lives under the given directory, either as a direct child
+     * or a distant grandchild.
+     * <p>
+     * Both files <em>must</em> have been resolved using
+     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+     * attacks.
+     *
+     * @hide
+     */
+    public static boolean contains(File[] dirs, File file) {
+        for (File dir : dirs) {
+            if (contains(dir, file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** {@hide} */
+    public static boolean contains(Collection<File> dirs, File file) {
+        for (File dir : dirs) {
+            if (contains(dir, file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test if a file lives under the given directory, either as a direct child
+     * or a distant grandchild.
+     * <p>
+     * Both files <em>must</em> have been resolved using
+     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+     * attacks.
+     *
+     * @hide
+     */
+    @TestApi
+    public static boolean contains(File dir, File file) {
+        if (dir == null || file == null) return false;
+        return contains(dir.getAbsolutePath(), file.getAbsolutePath());
+    }
+
+    /**
+     * Test if a file lives under the given directory, either as a direct child
+     * or a distant grandchild.
+     * <p>
+     * Both files <em>must</em> have been resolved using
+     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+     * attacks.
+     *
+     * @hide
+     */
+    public static boolean contains(String dirPath, String filePath) {
+        if (dirPath.equals(filePath)) {
+            return true;
+        }
+        if (!dirPath.endsWith("/")) {
+            dirPath += "/";
+        }
+        return filePath.startsWith(dirPath);
+    }
+
+    /** {@hide} */
+    public static boolean deleteContentsAndDir(File dir) {
+        if (deleteContents(dir)) {
+            return dir.delete();
+        } else {
+            return false;
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static boolean deleteContents(File dir) {
+        File[] files = dir.listFiles();
+        boolean success = true;
+        if (files != null) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    success &= deleteContents(file);
+                }
+                if (!file.delete()) {
+                    Log.w(TAG, "Failed to delete " + file);
+                    success = false;
+                }
+            }
+        }
+        return success;
+    }
+
+    private static boolean isValidExtFilenameChar(char c) {
+        switch (c) {
+            case '\0':
+            case '/':
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Check if given filename is valid for an ext4 filesystem.
+     *
+     * @hide
+     */
+    public static boolean isValidExtFilename(String name) {
+        return (name != null) && name.equals(buildValidExtFilename(name));
+    }
+
+    /**
+     * Mutate the given filename to make it valid for an ext4 filesystem,
+     * replacing any invalid characters with "_".
+     *
+     * @hide
+     */
+    public static String buildValidExtFilename(String name) {
+        if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
+            return "(invalid)";
+        }
+        final StringBuilder res = new StringBuilder(name.length());
+        for (int i = 0; i < name.length(); i++) {
+            final char c = name.charAt(i);
+            if (isValidExtFilenameChar(c)) {
+                res.append(c);
+            } else {
+                res.append('_');
+            }
+        }
+        trimFilename(res, 255);
+        return res.toString();
+    }
+
+    private static boolean isValidFatFilenameChar(char c) {
+        if ((0x00 <= c && c <= 0x1f)) {
+            return false;
+        }
+        switch (c) {
+            case '"':
+            case '*':
+            case '/':
+            case ':':
+            case '<':
+            case '>':
+            case '?':
+            case '\\':
+            case '|':
+            case 0x7F:
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Check if given filename is valid for a FAT filesystem.
+     *
+     * @hide
+     */
+    public static boolean isValidFatFilename(String name) {
+        return (name != null) && name.equals(buildValidFatFilename(name));
+    }
+
+    /**
+     * Mutate the given filename to make it valid for a FAT filesystem,
+     * replacing any invalid characters with "_".
+     *
+     * @hide
+     */
+    public static String buildValidFatFilename(String name) {
+        if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
+            return "(invalid)";
+        }
+        final StringBuilder res = new StringBuilder(name.length());
+        for (int i = 0; i < name.length(); i++) {
+            final char c = name.charAt(i);
+            if (isValidFatFilenameChar(c)) {
+                res.append(c);
+            } else {
+                res.append('_');
+            }
+        }
+        // Even though vfat allows 255 UCS-2 chars, we might eventually write to
+        // ext4 through a FUSE layer, so use that limit.
+        trimFilename(res, 255);
+        return res.toString();
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static String trimFilename(String str, int maxBytes) {
+        final StringBuilder res = new StringBuilder(str);
+        trimFilename(res, maxBytes);
+        return res.toString();
+    }
+
+    /** {@hide} */
+    private static void trimFilename(StringBuilder res, int maxBytes) {
+        byte[] raw = res.toString().getBytes(StandardCharsets.UTF_8);
+        if (raw.length > maxBytes) {
+            maxBytes -= 3;
+            while (raw.length > maxBytes) {
+                res.deleteCharAt(res.length() / 2);
+                raw = res.toString().getBytes(StandardCharsets.UTF_8);
+            }
+            res.insert(res.length() / 2, "...");
+        }
+    }
+
+    /** {@hide} */
+    public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
+        if (path == null) return null;
+        final File result = rewriteAfterRename(beforeDir, afterDir, new File(path));
+        return (result != null) ? result.getAbsolutePath() : null;
+    }
+
+    /** {@hide} */
+    public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) {
+        if (paths == null) return null;
+        final String[] result = new String[paths.length];
+        for (int i = 0; i < paths.length; i++) {
+            result[i] = rewriteAfterRename(beforeDir, afterDir, paths[i]);
+        }
+        return result;
+    }
+
+    /**
+     * Given a path under the "before" directory, rewrite it to live under the
+     * "after" directory. For example, {@code /before/foo/bar.txt} would become
+     * {@code /after/foo/bar.txt}.
+     *
+     * @hide
+     */
+    public static File rewriteAfterRename(File beforeDir, File afterDir, File file) {
+        if (file == null || beforeDir == null || afterDir == null) return null;
+        if (contains(beforeDir, file)) {
+            final String splice = file.getAbsolutePath().substring(
+                    beforeDir.getAbsolutePath().length());
+            return new File(afterDir, splice);
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    private static File buildUniqueFileWithExtension(File parent, String name, String ext)
+            throws FileNotFoundException {
+        File file = buildFile(parent, name, ext);
+
+        // If conflicting file, try adding counter suffix
+        int n = 0;
+        while (file.exists()) {
+            if (n++ >= 32) {
+                throw new FileNotFoundException("Failed to create unique file");
+            }
+            file = buildFile(parent, name + " (" + n + ")", ext);
+        }
+
+        return file;
+    }
+
+    /**
+     * Generates a unique file name under the given parent directory. If the display name doesn't
+     * have an extension that matches the requested MIME type, the default extension for that MIME
+     * type is appended. If a file already exists, the name is appended with a numerical value to
+     * make it unique.
+     *
+     * For example, the display name 'example' with 'text/plain' MIME might produce
+     * 'example.txt' or 'example (1).txt', etc.
+     *
+     * @throws FileNotFoundException
+     * @hide
+     */
+    public static File buildUniqueFile(File parent, String mimeType, String displayName)
+            throws FileNotFoundException {
+        final String[] parts = splitFileName(mimeType, displayName);
+        return buildUniqueFileWithExtension(parent, parts[0], parts[1]);
+    }
+
+    /** {@hide} */
+    public static File buildNonUniqueFile(File parent, String mimeType, String displayName) {
+        final String[] parts = splitFileName(mimeType, displayName);
+        return buildFile(parent, parts[0], parts[1]);
+    }
+
+    /**
+     * Generates a unique file name under the given parent directory, keeping
+     * any extension intact.
+     *
+     * @hide
+     */
+    public static File buildUniqueFile(File parent, String displayName)
+            throws FileNotFoundException {
+        final String name;
+        final String ext;
+
+        // Extract requested extension from display name
+        final int lastDot = displayName.lastIndexOf('.');
+        if (lastDot >= 0) {
+            name = displayName.substring(0, lastDot);
+            ext = displayName.substring(lastDot + 1);
+        } else {
+            name = displayName;
+            ext = null;
+        }
+
+        return buildUniqueFileWithExtension(parent, name, ext);
+    }
+
+    /**
+     * Splits file name into base name and extension.
+     * If the display name doesn't have an extension that matches the requested MIME type, the
+     * extension is regarded as a part of filename and default extension for that MIME type is
+     * appended.
+     *
+     * @hide
+     */
+    public static String[] splitFileName(String mimeType, String displayName) {
+        String name;
+        String ext;
+
+        if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+            name = displayName;
+            ext = null;
+        } else {
+            String mimeTypeFromExt;
+
+            // Extract requested extension from display name
+            final int lastDot = displayName.lastIndexOf('.');
+            if (lastDot >= 0) {
+                name = displayName.substring(0, lastDot);
+                ext = displayName.substring(lastDot + 1);
+                mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                        ext.toLowerCase());
+            } else {
+                name = displayName;
+                ext = null;
+                mimeTypeFromExt = null;
+            }
+
+            if (mimeTypeFromExt == null) {
+                mimeTypeFromExt = ContentResolver.MIME_TYPE_DEFAULT;
+            }
+
+            final String extFromMimeType;
+            if (ContentResolver.MIME_TYPE_DEFAULT.equals(mimeType)) {
+                extFromMimeType = null;
+            } else {
+                extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
+            }
+
+            if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
+                // Extension maps back to requested MIME type; allow it
+            } else {
+                // No match; insist that create file matches requested MIME
+                name = displayName;
+                ext = extFromMimeType;
+            }
+        }
+
+        if (ext == null) {
+            ext = "";
+        }
+
+        return new String[] { name, ext };
+    }
+
+    /** {@hide} */
+    private static File buildFile(File parent, String name, String ext) {
+        if (TextUtils.isEmpty(ext)) {
+            return new File(parent, name);
+        } else {
+            return new File(parent, name + "." + ext);
+        }
+    }
+
+    /** {@hide} */
+    public static @NonNull String[] listOrEmpty(@Nullable File dir) {
+        return (dir != null) ? ArrayUtils.defeatNullable(dir.list())
+                : EmptyArray.STRING;
+    }
+
+    /** {@hide} */
+    public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
+        return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
+                : ArrayUtils.EMPTY_FILE;
+    }
+
+    /** {@hide} */
+    public static @NonNull File[] listFilesOrEmpty(@Nullable File dir, FilenameFilter filter) {
+        return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles(filter))
+                : ArrayUtils.EMPTY_FILE;
+    }
+
+    /** {@hide} */
+    public static @Nullable File newFileOrNull(@Nullable String path) {
+        return (path != null) ? new File(path) : null;
+    }
+
+    /**
+     * Creates a directory with name {@code name} under an existing directory {@code baseDir} if it
+     * doesn't exist already. Returns a {@code File} object representing the directory if it exists
+     * and {@code null} if not.
+     *
+     * @hide
+     */
+    public static @Nullable File createDir(File baseDir, String name) {
+        final File dir = new File(baseDir, name);
+
+        return createDir(dir) ? dir : null;
+    }
+
+    /**
+     * Ensure the given directory exists, creating it if needed. This method is threadsafe.
+     *
+     * @return false if the directory doesn't exist and couldn't be created
+     *
+     * @hide
+     */
+    public static boolean createDir(File dir) {
+        if (dir.mkdir()) {
+            return true;
+        }
+
+        if (dir.exists()) {
+            return dir.isDirectory();
+        }
+
+        return false;
+    }
+
+    /**
+     * Round the given size of a storage device to a nice round power-of-two
+     * value, such as 256MB or 32GB. This avoids showing weird values like
+     * "29.5GB" in UI.
+     *
+     * Some storage devices are still using GiB (powers of 1024) over
+     * GB (powers of 1000) measurements and this method takes it into account.
+     *
+     * Round ranges:
+     * ...
+     * [256 GiB + 1; 512 GiB] -> 512 GB
+     * [512 GiB + 1; 1 TiB]   -> 1 TB
+     * [1 TiB + 1; 2 TiB]     -> 2 TB
+     * etc
+     *
+     * @hide
+     */
+    public static long roundStorageSize(long size) {
+        long val = 1;
+        long kiloPow = 1;
+        long kibiPow = 1;
+        while ((val * kibiPow) < size) {
+            val <<= 1;
+            if (val > 512) {
+                val = 1;
+                kibiPow *= 1024;
+                kiloPow *= 1000;
+            }
+        }
+        return val * kiloPow;
+    }
+
+    private static long toBytes(long value, String unit) {
+        unit = unit.toUpperCase();
+
+        if ("B".equals(unit)) {
+            return value;
+        }
+
+        if ("K".equals(unit) || "KB".equals(unit)) {
+            return DataUnit.KILOBYTES.toBytes(value);
+        }
+
+        if ("M".equals(unit) || "MB".equals(unit)) {
+            return DataUnit.MEGABYTES.toBytes(value);
+        }
+
+        if ("G".equals(unit) || "GB".equals(unit)) {
+            return DataUnit.GIGABYTES.toBytes(value);
+        }
+
+        if ("KI".equals(unit) || "KIB".equals(unit)) {
+            return DataUnit.KIBIBYTES.toBytes(value);
+        }
+
+        if ("MI".equals(unit) || "MIB".equals(unit)) {
+            return DataUnit.MEBIBYTES.toBytes(value);
+        }
+
+        if ("GI".equals(unit) || "GIB".equals(unit)) {
+            return DataUnit.GIBIBYTES.toBytes(value);
+        }
+
+        return Long.MIN_VALUE;
+    }
+
+    /**
+     * @param fmtSize The string that contains the size to be parsed. The
+     *   expected format is:
+     *
+     *   <p>"^((\\s*[-+]?[0-9]+)\\s*(B|K|KB|M|MB|G|GB|Ki|KiB|Mi|MiB|Gi|GiB)\\s*)$"
+     *
+     *   <p>For example: 10Kb, 500GiB, 100mb. The unit is not case sensitive.
+     *
+     * @return the size in bytes. If {@code fmtSize} has invalid format, it
+     *   returns {@link Long#MIN_VALUE}.
+     * @hide
+     */
+    public static long parseSize(@Nullable String fmtSize) {
+        if (fmtSize == null || fmtSize.isBlank()) {
+            return Long.MIN_VALUE;
+        }
+
+        int sign = 1;
+        fmtSize = fmtSize.trim();
+        char first = fmtSize.charAt(0);
+        if (first == '-' ||  first == '+') {
+            if (first == '-') {
+                sign = -1;
+            }
+
+            fmtSize = fmtSize.substring(1);
+        }
+
+        int index = 0;
+        // Find the last index of the value in fmtSize.
+        while (index < fmtSize.length() && Character.isDigit(fmtSize.charAt(index))) {
+            index++;
+        }
+
+        // Check if number and units are present.
+        if (index == 0 || index == fmtSize.length()) {
+            return Long.MIN_VALUE;
+        }
+
+        long value = sign * Long.valueOf(fmtSize.substring(0, index));
+        String unit = fmtSize.substring(index).trim();
+
+        return toBytes(value, unit);
+    }
+
+    /**
+     * Closes the given object quietly, ignoring any checked exceptions. Does
+     * nothing if the given object is {@code null}.
+     *
+     * @deprecated This method may suppress potentially significant exceptions, particularly when
+     *   closing writable resources. With a writable resource, a failure thrown from {@code close()}
+     *   should be considered as significant as a failure thrown from a write method because it may
+     *   indicate a failure to flush bytes to the underlying resource.
+     */
+    @Deprecated
+    public static void closeQuietly(@Nullable AutoCloseable closeable) {
+        IoUtils.closeQuietly(closeable);
+    }
+
+    /**
+     * Closes the given object quietly, ignoring any checked exceptions. Does
+     * nothing if the given object is {@code null}.
+     *
+     * @deprecated This method may suppress potentially significant exceptions, particularly when
+     *   closing writable resources. With a writable resource, a failure thrown from {@code close()}
+     *   should be considered as significant as a failure thrown from a write method because it may
+     *   indicate a failure to flush bytes to the underlying resource.
+     */
+    @Deprecated
+    public static void closeQuietly(@Nullable FileDescriptor fd) {
+        IoUtils.closeQuietly(fd);
+    }
+
+    /** {@hide} */
+    public static int translateModeStringToPosix(String mode) {
+        // Quick check for invalid chars
+        for (int i = 0; i < mode.length(); i++) {
+            switch (mode.charAt(i)) {
+                case 'r':
+                case 'w':
+                case 't':
+                case 'a':
+                    break;
+                default:
+                    throw new IllegalArgumentException("Bad mode: " + mode);
+            }
+        }
+
+        int res = 0;
+        if (mode.startsWith("rw")) {
+            res = O_RDWR | O_CREAT;
+        } else if (mode.startsWith("w")) {
+            res = O_WRONLY | O_CREAT;
+        } else if (mode.startsWith("r")) {
+            res = O_RDONLY;
+        } else {
+            throw new IllegalArgumentException("Bad mode: " + mode);
+        }
+        if (mode.indexOf('t') != -1) {
+            res |= O_TRUNC;
+        }
+        if (mode.indexOf('a') != -1) {
+            res |= O_APPEND;
+        }
+        return res;
+    }
+
+    /** {@hide} */
+    public static String translateModePosixToString(int mode) {
+        String res = "";
+        if ((mode & O_ACCMODE) == O_RDWR) {
+            res = "rw";
+        } else if ((mode & O_ACCMODE) == O_WRONLY) {
+            res = "w";
+        } else if ((mode & O_ACCMODE) == O_RDONLY) {
+            res = "r";
+        } else {
+            throw new IllegalArgumentException("Bad mode: " + mode);
+        }
+        if ((mode & O_TRUNC) == O_TRUNC) {
+            res += "t";
+        }
+        if ((mode & O_APPEND) == O_APPEND) {
+            res += "a";
+        }
+        return res;
+    }
+
+    /** {@hide} */
+    public static int translateModePosixToPfd(int mode) {
+        int res = 0;
+        if ((mode & O_ACCMODE) == O_RDWR) {
+            res = MODE_READ_WRITE;
+        } else if ((mode & O_ACCMODE) == O_WRONLY) {
+            res = MODE_WRITE_ONLY;
+        } else if ((mode & O_ACCMODE) == O_RDONLY) {
+            res = MODE_READ_ONLY;
+        } else {
+            throw new IllegalArgumentException("Bad mode: " + mode);
+        }
+        if ((mode & O_CREAT) == O_CREAT) {
+            res |= MODE_CREATE;
+        }
+        if ((mode & O_TRUNC) == O_TRUNC) {
+            res |= MODE_TRUNCATE;
+        }
+        if ((mode & O_APPEND) == O_APPEND) {
+            res |= MODE_APPEND;
+        }
+        return res;
+    }
+
+    /** {@hide} */
+    public static int translateModePfdToPosix(int mode) {
+        int res = 0;
+        if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
+            res = O_RDWR;
+        } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
+            res = O_WRONLY;
+        } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
+            res = O_RDONLY;
+        } else {
+            throw new IllegalArgumentException("Bad mode: " + mode);
+        }
+        if ((mode & MODE_CREATE) == MODE_CREATE) {
+            res |= O_CREAT;
+        }
+        if ((mode & MODE_TRUNCATE) == MODE_TRUNCATE) {
+            res |= O_TRUNC;
+        }
+        if ((mode & MODE_APPEND) == MODE_APPEND) {
+            res |= O_APPEND;
+        }
+        return res;
+    }
+
+    /** {@hide} */
+    public static int translateModeAccessToPosix(int mode) {
+        if (mode == F_OK) {
+            // There's not an exact mapping, so we attempt a read-only open to
+            // determine if a file exists
+            return O_RDONLY;
+        } else if ((mode & (R_OK | W_OK)) == (R_OK | W_OK)) {
+            return O_RDWR;
+        } else if ((mode & R_OK) == R_OK) {
+            return O_RDONLY;
+        } else if ((mode & W_OK) == W_OK) {
+            return O_WRONLY;
+        } else {
+            throw new IllegalArgumentException("Bad mode: " + mode);
+        }
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static ParcelFileDescriptor convertToModernFd(FileDescriptor fd) {
+        Context context = AppGlobals.getInitialApplication();
+        if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
+            // Never convert modern fd for MediaProvider, because this requires
+            // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
+            return null;
+        }
+
+        try (ParcelFileDescriptor dupFd = ParcelFileDescriptor.dup(fd)) {
+            return MediaStore.getOriginalMediaFormatFileDescriptor(context, dupFd);
+        } catch (Exception e) {
+            // Ignore error
+            return null;
+        }
+    }
+
+    private static int getMediaProviderAppId(Context context) {
+        if (sMediaProviderAppId != -1) {
+            return sMediaProviderAppId;
+        }
+
+        PackageManager pm = context.getPackageManager();
+        ProviderInfo provider = context.getPackageManager().resolveContentProvider(
+                MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                | PackageManager.MATCH_SYSTEM_ONLY);
+        if (provider == null) {
+            return -1;
+        }
+
+        sMediaProviderAppId = UserHandle.getAppId(provider.applicationInfo.uid);
+        return sMediaProviderAppId;
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static class MemoryPipe extends Thread implements AutoCloseable {
+        private final FileDescriptor[] pipe;
+        private final byte[] data;
+        private final boolean sink;
+
+        private MemoryPipe(byte[] data, boolean sink) throws IOException {
+            try {
+                this.pipe = Os.pipe();
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+            this.data = data;
+            this.sink = sink;
+        }
+
+        private MemoryPipe startInternal() {
+            super.start();
+            return this;
+        }
+
+        public static MemoryPipe createSource(byte[] data) throws IOException {
+            return new MemoryPipe(data, false).startInternal();
+        }
+
+        public static MemoryPipe createSink(byte[] data) throws IOException {
+            return new MemoryPipe(data, true).startInternal();
+        }
+
+        public FileDescriptor getFD() {
+            return sink ? pipe[1] : pipe[0];
+        }
+
+        public FileDescriptor getInternalFD() {
+            return sink ? pipe[0] : pipe[1];
+        }
+
+        @Override
+        public void run() {
+            final FileDescriptor fd = getInternalFD();
+            try {
+                int i = 0;
+                while (i < data.length) {
+                    if (sink) {
+                        i += Os.read(fd, data, i, data.length - i);
+                    } else {
+                        i += Os.write(fd, data, i, data.length - i);
+                    }
+                }
+            } catch (IOException | ErrnoException e) {
+                // Ignored
+            } finally {
+                if (sink) {
+                    SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
+                }
+                IoUtils.closeQuietly(fd);
+            }
+        }
+
+        @Override
+        public void close() throws Exception {
+            IoUtils.closeQuietly(getFD());
+        }
+    }
+}
diff --git a/android-34/android/os/GraphicsEnvironment.java b/android-34/android/os/GraphicsEnvironment.java
new file mode 100644
index 0000000..94971b8
--- /dev/null
+++ b/android-34/android/os/GraphicsEnvironment.java
@@ -0,0 +1,915 @@
+/*
+ * Copyright 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.os;
+
+import android.app.Activity;
+import android.app.GameManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import dalvik.system.VMRuntime;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * GraphicsEnvironment sets up necessary properties for the graphics environment of the
+ * application process.
+ * GraphicsEnvironment uses a bunch of settings global variables to determine the setup,
+ * the change of settings global variables will only take effect before setup() is called,
+ * and any subsequent change will not impact the current running processes.
+ *
+ * @hide
+ */
+public class GraphicsEnvironment {
+
+    private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
+
+    /**
+     * Returns the shared {@link GraphicsEnvironment} instance.
+     */
+    public static GraphicsEnvironment getInstance() {
+        return sInstance;
+    }
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "GraphicsEnvironment";
+    private static final String SYSTEM_DRIVER_NAME = "system";
+    private static final String SYSTEM_DRIVER_VERSION_NAME = "";
+    private static final long SYSTEM_DRIVER_VERSION_CODE = 0;
+    private static final String ANGLE_DRIVER_NAME = "angle";
+    private static final String ANGLE_DRIVER_VERSION_NAME = "";
+    private static final long ANGLE_DRIVER_VERSION_CODE = 0;
+
+    // System properties related to updatable graphics drivers.
+    private static final String PROPERTY_GFX_DRIVER_PRODUCTION = "ro.gfx.driver.0";
+    private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
+    private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time";
+
+    // Metadata flags within the <application> tag in the AndroidManifest.xml file.
+    private static final String METADATA_DRIVER_BUILD_TIME =
+            "com.android.graphics.driver.build_time";
+    private static final String METADATA_DEVELOPER_DRIVER_ENABLE =
+            "com.android.graphics.developerdriver.enable";
+    private static final String METADATA_INJECT_LAYERS_ENABLE =
+            "com.android.graphics.injectLayers.enable";
+
+    private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
+    private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
+
+    private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
+    private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
+            "android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
+    private static final String INTENT_KEY_A4A_TOAST_MESSAGE = "A4A Toast Message";
+
+    private static final int VULKAN_1_0 = 0x00400000;
+    private static final int VULKAN_1_1 = 0x00401000;
+    private static final int VULKAN_1_2 = 0x00402000;
+    private static final int VULKAN_1_3 = 0x00403000;
+
+    // Values for UPDATABLE_DRIVER_ALL_APPS
+    // 0: Default (Invalid values fallback to default as well)
+    // 1: All apps use updatable production driver
+    // 2: All apps use updatable prerelease driver
+    // 3: All apps use system graphics driver
+    private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT = 0;
+    private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER = 1;
+    private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2;
+    private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3;
+
+    // Values for ANGLE_GL_DRIVER_ALL_ANGLE
+    private static final int ANGLE_GL_DRIVER_ALL_ANGLE_ON = 1;
+    private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
+
+    // Values for ANGLE_GL_DRIVER_SELECTION_VALUES
+    private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
+    private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
+    private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+
+    private ClassLoader mClassLoader;
+    private String mLibrarySearchPaths;
+    private String mLibraryPermittedPaths;
+    private GameManager mGameManager;
+
+    private int mAngleOptInIndex = -1;
+    private boolean mEnabledByGameMode = false;
+
+    /**
+     * Set up GraphicsEnvironment
+     */
+    public void setup(Context context, Bundle coreSettings) {
+        final PackageManager pm = context.getPackageManager();
+        final String packageName = context.getPackageName();
+        final ApplicationInfo appInfoWithMetaData =
+                getAppInfoWithMetadata(context, pm, packageName);
+
+        mGameManager = context.getSystemService(GameManager.class);
+
+        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
+        setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
+        Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+
+        // Setup ANGLE and pass down ANGLE details to the C++ code
+        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
+        boolean useAngle = false;
+        if (setupAngle(context, coreSettings, pm, packageName)) {
+            if (shouldUseAngle(context, coreSettings, packageName)) {
+                useAngle = true;
+                setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE,
+                        0, packageName, getVulkanVersion(pm));
+            }
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+
+        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
+        if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
+            if (!useAngle) {
+                setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME,
+                        SYSTEM_DRIVER_VERSION_CODE,
+                        SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0),
+                        packageName, getVulkanVersion(pm));
+            }
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+
+        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "notifyGraphicsEnvironmentSetup");
+        if (mGameManager != null
+                && appInfoWithMetaData.category == ApplicationInfo.CATEGORY_GAME) {
+            mGameManager.notifyGraphicsEnvironmentSetup();
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+    }
+
+    /**
+     * Switch the system to use ANGLE as the default GLES driver.
+     */
+    public void toggleAngleAsSystemDriver(boolean enabled) {
+        nativeToggleAngleAsSystemDriver(enabled);
+    }
+
+    /**
+     * Query to determine if the Game Mode has enabled ANGLE.
+     */
+    private boolean isAngleEnabledByGameMode(Context context, String packageName) {
+        try {
+            final boolean gameModeEnabledAngle =
+                    (mGameManager != null) && mGameManager.isAngleEnabled(packageName);
+            Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
+            return gameModeEnabledAngle;
+        } catch (SecurityException e) {
+            Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled "
+                    + "for package: " + packageName);
+        }
+
+        return false;
+    }
+
+    /**
+     * Query to determine if ANGLE should be used
+     */
+    private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) {
+        if (TextUtils.isEmpty(packageName)) {
+            Log.v(TAG, "No package name specified; use the system driver");
+            return false;
+        }
+
+        return shouldUseAngleInternal(context, coreSettings, packageName);
+    }
+
+    private int getVulkanVersion(PackageManager pm) {
+        // PackageManager doesn't have an API to retrieve the version of a specific feature, and we
+        // need to avoid retrieving all system features here and looping through them.
+        if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_3)) {
+            return VULKAN_1_3;
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_2)) {
+            return VULKAN_1_2;
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_1)) {
+            return VULKAN_1_1;
+        }
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_0)) {
+            return VULKAN_1_0;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Check whether application is has set the manifest metadata for layer injection.
+     */
+    private boolean canInjectLayers(ApplicationInfo ai) {
+        return (ai.metaData != null && ai.metaData.getBoolean(METADATA_INJECT_LAYERS_ENABLE)
+                && setInjectLayersPrSetDumpable());
+    }
+
+    /**
+     * Store the class loader for namespace lookup later.
+     */
+    public void setLayerPaths(ClassLoader classLoader,
+                              String searchPaths,
+                              String permittedPaths) {
+        // We have to store these in the class because they are set up before we
+        // have access to the Context to properly set up GraphicsEnvironment
+        mClassLoader = classLoader;
+        mLibrarySearchPaths = searchPaths;
+        mLibraryPermittedPaths = permittedPaths;
+    }
+
+    /**
+     * Returns the debug layer paths from settings.
+     * Returns null if:
+     *     1) The application process is not debuggable or layer injection metadata flag is not
+     *        true; Or
+     *     2) ENABLE_GPU_DEBUG_LAYERS is not true; Or
+     *     3) Package name is not equal to GPU_DEBUG_APP.
+     */
+    public String getDebugLayerPathsFromSettings(
+            Bundle coreSettings, IPackageManager pm, String packageName,
+            ApplicationInfo ai) {
+        if (!debugLayerEnabled(coreSettings, packageName, ai)) {
+            return null;
+        }
+        Log.i(TAG, "GPU debug layers enabled for " + packageName);
+        String debugLayerPaths = "";
+
+        // Grab all debug layer apps and add to paths.
+        final String gpuDebugLayerApps =
+                coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP, "");
+        if (!gpuDebugLayerApps.isEmpty()) {
+            Log.i(TAG, "GPU debug layer apps: " + gpuDebugLayerApps);
+            // If a colon is present, treat this as multiple apps, so Vulkan and GLES
+            // layer apps can be provided at the same time.
+            final String[] layerApps = gpuDebugLayerApps.split(":");
+            for (int i = 0; i < layerApps.length; i++) {
+                String paths = getDebugLayerAppPaths(pm, layerApps[i]);
+                if (!paths.isEmpty()) {
+                    // Append the path so files placed in the app's base directory will
+                    // override the external path
+                    debugLayerPaths += paths + File.pathSeparator;
+                }
+            }
+        }
+        return debugLayerPaths;
+    }
+
+    /**
+     * Return the debug layer app's on-disk and in-APK lib directories
+     */
+    private String getDebugLayerAppPaths(IPackageManager pm, String packageName) {
+        final ApplicationInfo appInfo;
+        try {
+            appInfo = pm.getApplicationInfo(packageName, PackageManager.MATCH_ALL,
+                    UserHandle.myUserId());
+        } catch (RemoteException e) {
+            return "";
+        }
+        if (appInfo == null) {
+            Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
+            return "";
+        }
+
+        final String abi = chooseAbi(appInfo);
+        final StringBuilder sb = new StringBuilder();
+        sb.append(appInfo.nativeLibraryDir)
+            .append(File.pathSeparator)
+            .append(appInfo.sourceDir)
+            .append("!/lib/")
+            .append(abi);
+        final String paths = sb.toString();
+        if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths);
+
+        return paths;
+    }
+
+    private boolean debugLayerEnabled(Bundle coreSettings, String packageName, ApplicationInfo ai) {
+        // Only enable additional debug functionality if the following conditions are met:
+        // 1. App is debuggable or device is rooted or layer injection metadata flag is true
+        // 2. ENABLE_GPU_DEBUG_LAYERS is true
+        // 3. Package name is equal to GPU_DEBUG_APP
+        if (!isDebuggable() && !canInjectLayers(ai)) {
+            return false;
+        }
+        final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+        if (enable == 0) {
+            return false;
+        }
+        final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP, "");
+        if (packageName == null
+                || (gpuDebugApp.isEmpty() || packageName.isEmpty())
+                || !gpuDebugApp.equals(packageName)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set up layer search paths for all apps
+     */
+    private void setupGpuLayers(
+            Context context, Bundle coreSettings, PackageManager pm, String packageName,
+            ApplicationInfo ai) {
+        final boolean enabled = debugLayerEnabled(coreSettings, packageName, ai);
+        String layerPaths = "";
+        if (enabled) {
+            layerPaths = mLibraryPermittedPaths;
+
+            final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
+            Log.i(TAG, "Vulkan debug layer list: " + layers);
+            if (layers != null && !layers.isEmpty()) {
+                setDebugLayers(layers);
+            }
+
+            final String layersGLES =
+                    coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES);
+            Log.i(TAG, "GLES debug layer list: " + layersGLES);
+            if (layersGLES != null && !layersGLES.isEmpty()) {
+                setDebugLayersGLES(layersGLES);
+            }
+        }
+
+        // Include the app's lib directory in all cases
+        layerPaths += mLibrarySearchPaths;
+        setLayerPaths(mClassLoader, layerPaths);
+    }
+
+    private static List<String> getGlobalSettingsString(ContentResolver contentResolver,
+                                                        Bundle bundle,
+                                                        String globalSetting) {
+        final List<String> valueList;
+        final String settingsValue;
+
+        if (bundle != null) {
+            settingsValue = bundle.getString(globalSetting);
+        } else {
+            settingsValue = Settings.Global.getString(contentResolver, globalSetting);
+        }
+
+        if (settingsValue != null) {
+            valueList = new ArrayList<>(Arrays.asList(settingsValue.split(",")));
+        } else {
+            valueList = new ArrayList<>();
+        }
+
+        return valueList;
+    }
+
+    private static int getPackageIndex(String packageName, List<String> packages) {
+        for (int idx = 0; idx < packages.size(); idx++) {
+            if (packages.get(idx).equals(packageName)) {
+                return idx;
+            }
+        }
+
+        return -1;
+    }
+
+    private static ApplicationInfo getAppInfoWithMetadata(Context context,
+                                                          PackageManager pm, String packageName) {
+        ApplicationInfo ai;
+        try {
+            // Get the ApplicationInfo from PackageManager so that metadata fields present.
+            ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Unlikely to fail for applications, but in case of failure, fall back to use the
+            // ApplicationInfo from context directly.
+            ai = context.getApplicationInfo();
+        }
+        return ai;
+    }
+
+    /*
+     * Determine which GLES "driver" should be used for the package, taking into account the
+     * following factors (in priority order):
+     *
+     * 1) The semi-global switch (i.e. Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE; which is set by
+     *    the "angle_gl_driver_all_angle" setting; which forces a driver for all processes that
+     *    start after the Java run time is up), if it forces a choice;
+     * 2) The per-application switch (i.e. Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS and
+     *    Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES; which corresponds to the
+     *    “angle_gl_driver_selection_pkgs” and “angle_gl_driver_selection_values” settings); if it
+     *    forces a choice;
+     * 3) Use ANGLE if isAngleEnabledByGameMode() returns true;
+     */
+    private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) {
+        // Make sure we have a good package name
+        if (TextUtils.isEmpty(packageName)) {
+            return false;
+        }
+
+        // Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE
+        // should be forced on or off for "all appplications"
+        final int allUseAngle;
+        if (bundle != null) {
+            allUseAngle = bundle.getInt(Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE);
+        } else {
+            ContentResolver contentResolver = context.getContentResolver();
+            allUseAngle = Settings.Global.getInt(contentResolver,
+                    Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE, ANGLE_GL_DRIVER_ALL_ANGLE_OFF);
+        }
+        if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
+            Log.v(TAG, "Turn on ANGLE for all applications.");
+            return true;
+        }
+
+        // Get the per-application settings lists
+        final ContentResolver contentResolver = context.getContentResolver();
+        final List<String> optInPackages = getGlobalSettingsString(
+                contentResolver, bundle, Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS);
+        final List<String> optInValues = getGlobalSettingsString(
+                contentResolver, bundle, Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES);
+        Log.v(TAG, "Currently set values for:");
+        Log.v(TAG, "  angle_gl_driver_selection_pkgs=" + optInPackages);
+        Log.v(TAG, "  angle_gl_driver_selection_values=" + optInValues);
+
+        mEnabledByGameMode = isAngleEnabledByGameMode(context, packageName);
+
+        // Make sure we have good settings to use
+        if (optInPackages.size() != optInValues.size()) {
+            Log.v(TAG,
+                    "Global.Settings values are invalid: "
+                        + "number of packages: "
+                            + optInPackages.size() + ", "
+                        + "number of values: "
+                            + optInValues.size());
+            return mEnabledByGameMode;
+        }
+
+        // See if this application is listed in the per-application settings list
+        final int pkgIndex = getPackageIndex(packageName, optInPackages);
+
+        if (pkgIndex < 0) {
+            Log.v(TAG, packageName + " is not listed in per-application setting");
+            return mEnabledByGameMode;
+        }
+        mAngleOptInIndex = pkgIndex;
+
+        // The application IS listed in the per-application settings list; and so use the
+        // setting--choosing the current system driver if the setting is "default"
+        String optInValue = optInValues.get(pkgIndex);
+        Log.v(TAG,
+                "ANGLE Developer option for '" + packageName + "' "
+                        + "set to: '" + optInValue + "'");
+        if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
+            return true;
+        } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
+            return false;
+        } else {
+            // The user either chose default or an invalid value; go with the default driver or what
+            // the game mode indicates
+            return mEnabledByGameMode;
+        }
+    }
+
+    /**
+     * Get the ANGLE package name.
+     */
+    private String getAnglePackageName(PackageManager pm) {
+        final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID);
+
+        final List<ResolveInfo> resolveInfos =
+                pm.queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
+        if (resolveInfos.size() != 1) {
+            Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+                    + resolveInfos.size());
+            for (ResolveInfo resolveInfo : resolveInfos) {
+                Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+            }
+            return "";
+        }
+
+        // Must be exactly 1 ANGLE PKG found to get here.
+        return resolveInfos.get(0).activityInfo.packageName;
+    }
+
+    /**
+     * Check for ANGLE debug package, but only for apps that can load them.
+     * An application can load ANGLE debug package if it is a debuggable application, or
+     * the device is debuggable.
+     */
+    private String getAngleDebugPackage(Context context, Bundle coreSettings) {
+        if (!isDebuggable()) {
+            return "";
+        }
+        final String debugPackage;
+
+        if (coreSettings != null) {
+            debugPackage =
+                    coreSettings.getString(Settings.Global.ANGLE_DEBUG_PACKAGE);
+        } else {
+            ContentResolver contentResolver = context.getContentResolver();
+            debugPackage = Settings.Global.getString(contentResolver,
+                    Settings.Global.ANGLE_DEBUG_PACKAGE);
+        }
+        if (TextUtils.isEmpty(debugPackage)) {
+            return "";
+        }
+        return debugPackage;
+    }
+
+    /**
+     * Determine whether ANGLE should be used, set it up if so, and pass ANGLE details down to
+     * the C++ GraphicsEnv class.
+     *
+     * If ANGLE will be used, GraphicsEnv::setAngleInfo() will be called to enable ANGLE to be
+     * properly used.
+     *
+     * @param context
+     * @param bundle
+     * @param pm
+     * @param packageName - package name of the application.
+     * @return true: ANGLE setup successfully
+     *         false: ANGLE not setup (not on allowlist, ANGLE not present, etc.)
+     */
+    private boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
+            String packageName) {
+
+        if (!shouldUseAngle(context, bundle, packageName)) {
+            return false;
+        }
+
+        ApplicationInfo angleInfo = null;
+
+        // If the developer has specified a debug package over ADB, attempt to find it
+        String anglePkgName = getAngleDebugPackage(context, bundle);
+        if (!anglePkgName.isEmpty()) {
+            Log.v(TAG, "ANGLE debug package enabled: " + anglePkgName);
+            try {
+                // Note the debug package does not have to be pre-installed
+                angleInfo = pm.getApplicationInfo(anglePkgName, 0);
+            } catch (PackageManager.NameNotFoundException e) {
+                // If the debug package is specified but not found, abort.
+                Log.v(TAG, "ANGLE debug package '" + anglePkgName + "' not installed");
+                return false;
+            }
+        }
+
+        // Otherwise, check to see if ANGLE is properly installed
+        if (angleInfo == null) {
+            anglePkgName = getAnglePackageName(pm);
+            if (TextUtils.isEmpty(anglePkgName)) {
+                Log.v(TAG, "Failed to find ANGLE package.");
+                return false;
+            }
+
+            Log.v(TAG, "ANGLE package enabled: " + anglePkgName);
+            try {
+                // Production ANGLE libraries must be pre-installed as a system app
+                angleInfo = pm.getApplicationInfo(anglePkgName,
+                        PackageManager.MATCH_SYSTEM_ONLY);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.v(TAG, "ANGLE package '" + anglePkgName + "' not installed");
+                return false;
+            }
+        }
+
+        final String abi = chooseAbi(angleInfo);
+
+        // Build a path that includes installed native libs and APK
+        final String paths = angleInfo.nativeLibraryDir
+                + File.pathSeparator
+                + angleInfo.sourceDir
+                + "!/lib/"
+                + abi;
+
+        if (DEBUG) {
+            Log.d(TAG, "ANGLE package libs: " + paths);
+        }
+
+        // If we make it to here, ANGLE will be used.  Call setAngleInfo() with the package name,
+        // and features to use.
+        final String[] features = getAngleEglFeatures(context, bundle);
+        setAngleInfo(paths, packageName, ANGLE_GL_DRIVER_CHOICE_ANGLE, features);
+
+        return true;
+    }
+
+    /**
+     * Determine if the "ANGLE In Use" dialog box should be shown.
+     */
+    private boolean shouldShowAngleInUseDialogBox(Context context) {
+        try {
+            ContentResolver contentResolver = context.getContentResolver();
+            final int showDialogBox = Settings.Global.getInt(contentResolver,
+                    Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX);
+
+            return (showDialogBox == 1);
+        } catch (Settings.SettingNotFoundException | SecurityException e) {
+            // Do nothing and move on
+        }
+
+        // No setting, so assume false
+        return false;
+    }
+
+    /**
+     * Determine if ANGLE will be used and setup the environment
+     */
+    private boolean setupAndUseAngle(Context context, String packageName) {
+        // Need to make sure we are evaluating ANGLE usage for the correct circumstances
+        if (!setupAngle(context, null, context.getPackageManager(), packageName)) {
+            Log.v(TAG, "Package '" + packageName + "' should not use ANGLE");
+            return false;
+        }
+
+        final boolean useAngle = getShouldUseAngle(packageName);
+        Log.v(TAG, "Package '" + packageName + "' should use ANGLE = '" + useAngle + "'");
+
+        return useAngle;
+    }
+
+    /**
+     * Show the ANGLE in Use Dialog Box
+     * @param context
+     */
+    public void showAngleInUseDialogBox(Context context) {
+        final String packageName = context.getPackageName();
+
+        if (shouldShowAngleInUseDialogBox(context) && setupAndUseAngle(context, packageName)) {
+            final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE);
+            String anglePkg = getAnglePackageName(context.getPackageManager());
+            intent.setPackage(anglePkg);
+
+            context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    Bundle results = getResultExtras(true);
+
+                    String toastMsg = results.getString(INTENT_KEY_A4A_TOAST_MESSAGE);
+                    final Toast toast = Toast.makeText(context, toastMsg, Toast.LENGTH_LONG);
+                    toast.show();
+                }
+            }, null, Activity.RESULT_OK, null, null);
+        }
+    }
+
+    private String[] getAngleEglFeatures(Context context, Bundle coreSettings) {
+        if (mAngleOptInIndex < 0) {
+            return null;
+        }
+
+        final List<String> featuresLists = getGlobalSettingsString(
+                context.getContentResolver(), coreSettings, Settings.Global.ANGLE_EGL_FEATURES);
+        if (featuresLists.size() <= mAngleOptInIndex) {
+            return null;
+        }
+        return featuresLists.get(mAngleOptInIndex).split(":");
+    }
+
+    /**
+     * Return the driver package name to use. Return null for system driver.
+     */
+    private String chooseDriverInternal(Bundle coreSettings, ApplicationInfo ai) {
+        final String productionDriver = SystemProperties.get(PROPERTY_GFX_DRIVER_PRODUCTION);
+        final boolean hasProductionDriver = productionDriver != null && !productionDriver.isEmpty();
+
+        final String prereleaseDriver = SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE);
+        final boolean hasPrereleaseDriver = prereleaseDriver != null && !prereleaseDriver.isEmpty();
+
+        if (!hasProductionDriver && !hasPrereleaseDriver) {
+            Log.v(TAG, "Neither updatable production driver nor prerelease driver is supported.");
+            return null;
+        }
+
+        // To minimize risk of driver updates crippling the device beyond user repair, never use the
+        // updatable drivers for privileged or non-updated system apps. Presumably pre-installed
+        // apps were tested thoroughly with the system driver.
+        if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
+            if (DEBUG) {
+                Log.v(TAG,
+                        "Ignore updatable driver package for privileged/non-updated system app.");
+            }
+            return null;
+        }
+
+        final boolean enablePrereleaseDriver =
+                (ai.metaData != null && ai.metaData.getBoolean(METADATA_DEVELOPER_DRIVER_ENABLE))
+                || isDebuggable();
+
+        // Priority of updatable driver settings on confliction (Higher priority comes first):
+        // 1. UPDATABLE_DRIVER_ALL_APPS
+        // 2. UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS
+        // 3. UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS
+        // 4. UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS
+        // 5. UPDATABLE_DRIVER_PRODUCTION_DENYLIST
+        // 6. UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST
+        switch (coreSettings.getInt(Settings.Global.UPDATABLE_DRIVER_ALL_APPS, 0)) {
+            case UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF:
+                Log.v(TAG, "The updatable driver is turned off on this device.");
+                return null;
+            case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER:
+                Log.v(TAG, "All apps opt in to use updatable production driver.");
+                return hasProductionDriver ? productionDriver : null;
+            case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER:
+                Log.v(TAG, "All apps opt in to use updatable prerelease driver.");
+                return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
+            case UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT:
+            default:
+                break;
+        }
+
+        final String appPackageName = ai.packageName;
+        if (getGlobalSettingsString(null, coreSettings,
+                                    Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS)
+                        .contains(appPackageName)) {
+            Log.v(TAG, "App opts out for updatable production driver.");
+            return null;
+        }
+
+        if (getGlobalSettingsString(
+                    null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS)
+                        .contains(appPackageName)) {
+            Log.v(TAG, "App opts in for updatable prerelease driver.");
+            return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null;
+        }
+
+        // Early return here since the rest logic is only for updatable production Driver.
+        if (!hasProductionDriver) {
+            Log.v(TAG, "Updatable production driver is not supported on the device.");
+            return null;
+        }
+
+        final boolean isOptIn =
+                getGlobalSettingsString(null, coreSettings,
+                                        Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS)
+                        .contains(appPackageName);
+        final List<String> allowlist =
+                getGlobalSettingsString(null, coreSettings,
+                                        Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST);
+        if (!isOptIn && allowlist.indexOf(UPDATABLE_DRIVER_ALLOWLIST_ALL) != 0
+                && !allowlist.contains(appPackageName)) {
+            Log.v(TAG, "App is not on the allowlist for updatable production driver.");
+            return null;
+        }
+
+        // If the application is not opted-in, then check whether it's on the denylist,
+        // terminate early if it's on the denylist and fallback to system driver.
+        if (!isOptIn
+                && getGlobalSettingsString(
+                        null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST)
+                           .contains(appPackageName)) {
+            Log.v(TAG, "App is on the denylist for updatable production driver.");
+            return null;
+        }
+
+        return productionDriver;
+    }
+
+    /**
+     * Choose whether the current process should use the builtin or an updated driver.
+     */
+    private boolean chooseDriver(
+            Context context, Bundle coreSettings, PackageManager pm, String packageName,
+            ApplicationInfo ai) {
+        final String driverPackageName = chooseDriverInternal(coreSettings, ai);
+        if (driverPackageName == null) {
+            return false;
+        }
+
+        final PackageInfo driverPackageInfo;
+        try {
+            driverPackageInfo = pm.getPackageInfo(driverPackageName,
+                    PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "updatable driver package '" + driverPackageName + "' not installed");
+            return false;
+        }
+
+        // O drivers are restricted to the sphal linker namespace, so don't try to use
+        // packages unless they declare they're compatible with that restriction.
+        final ApplicationInfo driverAppInfo = driverPackageInfo.applicationInfo;
+        if (driverAppInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+            if (DEBUG) {
+                Log.w(TAG, "updatable driver package is not compatible with O");
+            }
+            return false;
+        }
+
+        final String abi = chooseAbi(driverAppInfo);
+        if (abi == null) {
+            if (DEBUG) {
+                // This is the normal case for the pre-installed empty driver package, don't spam
+                if (driverAppInfo.isUpdatedSystemApp()) {
+                    Log.w(TAG, "Updatable driver package has no compatible native libraries");
+                }
+            }
+            return false;
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(driverAppInfo.nativeLibraryDir)
+          .append(File.pathSeparator);
+        sb.append(driverAppInfo.sourceDir)
+          .append("!/lib/")
+          .append(abi);
+        final String paths = sb.toString();
+        final String sphalLibraries = getSphalLibraries(context, driverPackageName);
+        Log.v(TAG, "Updatable driver package search path: " + paths
+                + ", required sphal libraries: " + sphalLibraries);
+        setDriverPathAndSphalLibraries(paths, sphalLibraries);
+
+        if (driverAppInfo.metaData == null) {
+            throw new NullPointerException("apk's meta-data cannot be null");
+        }
+
+        String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME);
+        if (driverBuildTime == null || driverBuildTime.length() <= 1) {
+            Log.w(TAG, "com.android.graphics.driver.build_time is not set");
+            driverBuildTime = "L0";
+        }
+        // driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456.
+        // Long.parseLong will throw if the meta-data "driver_build_time" is not set properly.
+        setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode,
+                Long.parseLong(driverBuildTime.substring(1)), packageName, 0);
+
+        return true;
+    }
+
+    private static String chooseAbi(ApplicationInfo ai) {
+        final String isa = VMRuntime.getCurrentInstructionSet();
+        if (ai.primaryCpuAbi != null &&
+                isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
+            return ai.primaryCpuAbi;
+        }
+        if (ai.secondaryCpuAbi != null &&
+                isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) {
+            return ai.secondaryCpuAbi;
+        }
+        return null;
+    }
+
+    private String getSphalLibraries(Context context, String driverPackageName) {
+        try {
+            final Context driverContext =
+                    context.createPackageContext(driverPackageName, Context.CONTEXT_RESTRICTED);
+            final BufferedReader reader = new BufferedReader(new InputStreamReader(
+                    driverContext.getAssets().open(UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME)));
+            final ArrayList<String> assetStrings = new ArrayList<>();
+            for (String assetString; (assetString = reader.readLine()) != null;) {
+                assetStrings.add(assetString);
+            }
+            return String.join(":", assetStrings);
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) {
+                Log.w(TAG, "Driver package '" + driverPackageName + "' not installed");
+            }
+        } catch (IOException e) {
+            if (DEBUG) {
+                Log.w(TAG, "Failed to load '" + UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME + "'");
+            }
+        }
+        return "";
+    }
+
+    private static native boolean isDebuggable();
+    private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
+    private static native void setDebugLayers(String layers);
+    private static native void setDebugLayersGLES(String layers);
+    private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
+    private static native void setGpuStats(String driverPackageName, String driverVersionName,
+            long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
+    private static native void setAngleInfo(String path, String appPackage,
+            String devOptIn, String[] features);
+    private static native boolean getShouldUseAngle(String packageName);
+    private static native boolean setInjectLayersPrSetDumpable();
+    private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
+
+    /**
+     * Hint for GraphicsEnvironment that an activity is launching on the process.
+     * Then the app process is allowed to send stats to GpuStats module.
+     */
+    public static native void hintActivityLaunch();
+}
diff --git a/android-34/android/os/Handler.java b/android-34/android/os/Handler.java
new file mode 100644
index 0000000..ceaf337
--- /dev/null
+++ b/android-34/android/os/Handler.java
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
+import android.util.Printer;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * A Handler allows you to send and process {@link Message} and Runnable
+ * objects associated with a thread's {@link MessageQueue}.  Each Handler
+ * instance is associated with a single thread and that thread's message
+ * queue. When you create a new Handler it is bound to a {@link Looper}.
+ * It will deliver messages and runnables to that Looper's message
+ * queue and execute them on that Looper's thread.
+ *
+ * <p>There are two main uses for a Handler: (1) to schedule messages and
+ * runnables to be executed at some point in the future; and (2) to enqueue
+ * an action to be performed on a different thread than your own.
+ *
+ * <p>Scheduling messages is accomplished with the
+ * {@link #post}, {@link #postAtTime(Runnable, long)},
+ * {@link #postDelayed}, {@link #sendEmptyMessage},
+ * {@link #sendMessage}, {@link #sendMessageAtTime}, and
+ * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
+ * you to enqueue Runnable objects to be called by the message queue when
+ * they are received; the <em>sendMessage</em> versions allow you to enqueue
+ * a {@link Message} object containing a bundle of data that will be
+ * processed by the Handler's {@link #handleMessage} method (requiring that
+ * you implement a subclass of Handler).
+ * 
+ * <p>When posting or sending to a Handler, you can either
+ * allow the item to be processed as soon as the message queue is ready
+ * to do so, or specify a delay before it gets processed or absolute time for
+ * it to be processed.  The latter two allow you to implement timeouts,
+ * ticks, and other timing-based behavior.
+ * 
+ * <p>When a
+ * process is created for your application, its main thread is dedicated to
+ * running a message queue that takes care of managing the top-level
+ * application objects (activities, broadcast receivers, etc) and any windows
+ * they create.  You can create your own threads, and communicate back with
+ * the main application thread through a Handler.  This is done by calling
+ * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
+ * your new thread.  The given Runnable or Message will then be scheduled
+ * in the Handler's message queue and processed when appropriate.
+ */
+public class Handler {
+    /*
+     * Set this flag to true to detect anonymous, local or member classes
+     * that extend this Handler class and that are not static. These kind
+     * of classes can potentially create leaks.
+     */
+    private static final boolean FIND_POTENTIAL_LEAKS = false;
+    private static final String TAG = "Handler";
+    private static Handler MAIN_THREAD_HANDLER = null;
+
+    /**
+     * Callback interface you can use when instantiating a Handler to avoid
+     * having to implement your own subclass of Handler.
+     */
+    public interface Callback {
+        /**
+         * @param msg A {@link android.os.Message Message} object
+         * @return True if no further handling is desired
+         */
+        boolean handleMessage(@NonNull Message msg);
+    }
+    
+    /**
+     * Subclasses must implement this to receive messages.
+     */
+    public void handleMessage(@NonNull Message msg) {
+    }
+    
+    /**
+     * Handle system messages here.
+     */
+    public void dispatchMessage(@NonNull Message msg) {
+        if (msg.callback != null) {
+            handleCallback(msg);
+        } else {
+            if (mCallback != null) {
+                if (mCallback.handleMessage(msg)) {
+                    return;
+                }
+            }
+            handleMessage(msg);
+        }
+    }
+
+    /**
+     * Default constructor associates this handler with the {@link Looper} for the
+     * current thread.
+     *
+     * If this thread does not have a looper, this handler won't be able to receive messages
+     * so an exception is thrown.
+     *
+     * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs
+     *   where operations are silently lost (if the Handler is not expecting new tasks and quits),
+     *   crashes (if a handler is sometimes created on a thread without a Looper active), or race
+     *   conditions, where the thread a handler is associated with is not what the author
+     *   anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
+     *   explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+     *   similar. If the implicit thread local behavior is required for compatibility, use
+     *   {@code new Handler(Looper.myLooper())} to make it clear to readers.
+     *
+     */
+    @Deprecated
+    public Handler() {
+        this(null, false);
+    }
+
+    /**
+     * Constructor associates this handler with the {@link Looper} for the
+     * current thread and takes a callback interface in which you can handle
+     * messages.
+     *
+     * If this thread does not have a looper, this handler won't be able to receive messages
+     * so an exception is thrown.
+     *
+     * @param callback The callback interface in which to handle messages, or null.
+     *
+     * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs
+     *   where operations are silently lost (if the Handler is not expecting new tasks and quits),
+     *   crashes (if a handler is sometimes created on a thread without a Looper active), or race
+     *   conditions, where the thread a handler is associated with is not what the author
+     *   anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
+     *   explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+     *   similar. If the implicit thread local behavior is required for compatibility, use
+     *   {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers.
+     */
+    @Deprecated
+    public Handler(@Nullable Callback callback) {
+        this(callback, false);
+    }
+
+    /**
+     * Use the provided {@link Looper} instead of the default one.
+     *
+     * @param looper The looper, must not be null.
+     */
+    public Handler(@NonNull Looper looper) {
+        this(looper, null, false);
+    }
+
+    /**
+     * Use the provided {@link Looper} instead of the default one and take a callback
+     * interface in which to handle messages.
+     *
+     * @param looper The looper, must not be null.
+     * @param callback The callback interface in which to handle messages, or null.
+     */
+    public Handler(@NonNull Looper looper, @Nullable Callback callback) {
+        this(looper, callback, false);
+    }
+
+    /**
+     * Use the {@link Looper} for the current thread
+     * and set whether the handler should be asynchronous.
+     *
+     * Handlers are synchronous by default unless this constructor is used to make
+     * one that is strictly asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with respect to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+     *
+     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public Handler(boolean async) {
+        this(null, async);
+    }
+
+    /**
+     * Use the {@link Looper} for the current thread with the specified callback interface
+     * and set whether the handler should be asynchronous.
+     *
+     * Handlers are synchronous by default unless this constructor is used to make
+     * one that is strictly asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with respect to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+     *
+     * @param callback The callback interface in which to handle messages, or null.
+     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+     *
+     * @hide
+     */
+    public Handler(@Nullable Callback callback, boolean async) {
+        if (FIND_POTENTIAL_LEAKS) {
+            final Class<? extends Handler> klass = getClass();
+            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
+                    (klass.getModifiers() & Modifier.STATIC) == 0) {
+                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
+                    klass.getCanonicalName());
+            }
+        }
+
+        mLooper = Looper.myLooper();
+        if (mLooper == null) {
+            throw new RuntimeException(
+                "Can't create handler inside thread " + Thread.currentThread()
+                        + " that has not called Looper.prepare()");
+        }
+        mQueue = mLooper.mQueue;
+        mCallback = callback;
+        mAsynchronous = async;
+        mIsShared = false;
+    }
+
+    /**
+     * Use the provided {@link Looper} instead of the default one and take a callback
+     * interface in which to handle messages.  Also set whether the handler
+     * should be asynchronous.
+     *
+     * Handlers are synchronous by default unless this constructor is used to make
+     * one that is strictly asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with respect to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by conditions such as display vsync.
+     *
+     * @param looper The looper, must not be null.
+     * @param callback The callback interface in which to handle messages, or null.
+     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
+        this(looper, callback, async, /* shared= */ false);
+    }
+
+    /** @hide */
+    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
+            boolean shared) {
+        mLooper = looper;
+        mQueue = looper.mQueue;
+        mCallback = callback;
+        mAsynchronous = async;
+        mIsShared = shared;
+    }
+
+    /**
+     * Create a new Handler whose posted messages and runnables are not subject to
+     * synchronization barriers such as display vsync.
+     *
+     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
+     * but not necessarily with respect to messages from other Handlers.</p>
+     *
+     * @see #createAsync(Looper, Callback) to create an async Handler with custom message handling.
+     *
+     * @param looper the Looper that the new Handler should be bound to
+     * @return a new async Handler instance
+     */
+    @NonNull
+    public static Handler createAsync(@NonNull Looper looper) {
+        if (looper == null) throw new NullPointerException("looper must not be null");
+        return new Handler(looper, null, true);
+    }
+
+    /**
+     * Create a new Handler whose posted messages and runnables are not subject to
+     * synchronization barriers such as display vsync.
+     *
+     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
+     * but not necessarily with respect to messages from other Handlers.</p>
+     *
+     * @see #createAsync(Looper) to create an async Handler without custom message handling.
+     *
+     * @param looper the Looper that the new Handler should be bound to
+     * @return a new async Handler instance
+     */
+    @NonNull
+    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
+        if (looper == null) throw new NullPointerException("looper must not be null");
+        if (callback == null) throw new NullPointerException("callback must not be null");
+        return new Handler(looper, callback, true);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @NonNull
+    public static Handler getMain() {
+        if (MAIN_THREAD_HANDLER == null) {
+            MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
+        }
+        return MAIN_THREAD_HANDLER;
+    }
+
+    /** @hide */
+    @NonNull
+    public static Handler mainIfNull(@Nullable Handler handler) {
+        return handler == null ? getMain() : handler;
+    }
+
+    /** {@hide} */
+    @NonNull
+    public String getTraceName(@NonNull Message message) {
+        if (message.callback instanceof TraceNameSupplier) {
+            return ((TraceNameSupplier) message.callback).getTraceName();
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getName()).append(": ");
+        if (message.callback != null) {
+            sb.append(message.callback.getClass().getName());
+        } else {
+            sb.append("#").append(message.what);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns a string representing the name of the specified message.
+     * The default implementation will either return the class name of the
+     * message callback if any, or the hexadecimal representation of the
+     * message "what" field.
+     *  
+     * @param message The message whose name is being queried 
+     */
+    @NonNull
+    public String getMessageName(@NonNull Message message) {
+        if (message.callback != null) {
+            return message.callback.getClass().getName();
+        }
+        return "0x" + Integer.toHexString(message.what);
+    }
+
+    /**
+     * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
+     * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
+     *  If you don't want that facility, just call Message.obtain() instead.
+     */
+    @NonNull
+    public final Message obtainMessage()
+    {
+        return Message.obtain(this);
+    }
+
+    /**
+     * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message.
+     * 
+     * @param what Value to assign to the returned Message.what field.
+     * @return A Message from the global message pool.
+     */
+    @NonNull
+    public final Message obtainMessage(int what)
+    {
+        return Message.obtain(this, what);
+    }
+    
+    /**
+     * 
+     * Same as {@link #obtainMessage()}, except that it also sets the what and obj members 
+     * of the returned Message.
+     * 
+     * @param what Value to assign to the returned Message.what field.
+     * @param obj Value to assign to the returned Message.obj field.
+     * @return A Message from the global message pool.
+     */
+    @NonNull
+    public final Message obtainMessage(int what, @Nullable Object obj) {
+        return Message.obtain(this, what, obj);
+    }
+
+    /**
+     * 
+     * Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned
+     * Message.
+     * @param what Value to assign to the returned Message.what field.
+     * @param arg1 Value to assign to the returned Message.arg1 field.
+     * @param arg2 Value to assign to the returned Message.arg2 field.
+     * @return A Message from the global message pool.
+     */
+    @NonNull
+    public final Message obtainMessage(int what, int arg1, int arg2)
+    {
+        return Message.obtain(this, what, arg1, arg2);
+    }
+    
+    /**
+     * 
+     * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the 
+     * returned Message.
+     * @param what Value to assign to the returned Message.what field.
+     * @param arg1 Value to assign to the returned Message.arg1 field.
+     * @param arg2 Value to assign to the returned Message.arg2 field.
+     * @param obj Value to assign to the returned Message.obj field.
+     * @return A Message from the global message pool.
+     */
+    @NonNull
+    public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {
+        return Message.obtain(this, what, arg1, arg2, obj);
+    }
+
+    /**
+     * Causes the Runnable r to be added to the message queue.
+     * The runnable will be run on the thread to which this handler is 
+     * attached. 
+     *  
+     * @param r The Runnable that will be executed.
+     * 
+     * @return Returns true if the Runnable was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+    public final boolean post(@NonNull Runnable r) {
+       return  sendMessageDelayed(getPostMessage(r), 0);
+    }
+    
+    /**
+     * Causes the Runnable r to be added to the message queue, to be run
+     * at a specific time given by <var>uptimeMillis</var>.
+     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+     * Time spent in deep sleep will add an additional delay to execution.
+     * The runnable will be run on the thread to which this handler is attached.
+     *
+     * @param r The Runnable that will be executed.
+     * @param uptimeMillis The absolute time at which the callback should run,
+     *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
+     *  
+     * @return Returns true if the Runnable was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the Runnable will be processed -- if
+     *         the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     */
+    public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
+        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
+    }
+    
+    /**
+     * Causes the Runnable r to be added to the message queue, to be run
+     * at a specific time given by <var>uptimeMillis</var>.
+     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+     * Time spent in deep sleep will add an additional delay to execution.
+     * The runnable will be run on the thread to which this handler is attached.
+     *
+     * @param r The Runnable that will be executed.
+     * @param token An instance which can be used to cancel {@code r} via
+     *         {@link #removeCallbacksAndMessages}.
+     * @param uptimeMillis The absolute time at which the callback should run,
+     *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
+     * 
+     * @return Returns true if the Runnable was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the Runnable will be processed -- if
+     *         the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     *         
+     * @see android.os.SystemClock#uptimeMillis
+     */
+    public final boolean postAtTime(
+            @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
+        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
+    }
+    
+    /**
+     * Causes the Runnable r to be added to the message queue, to be run
+     * after the specified amount of time elapses.
+     * The runnable will be run on the thread to which this handler
+     * is attached.
+     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+     * Time spent in deep sleep will add an additional delay to execution.
+     *  
+     * @param r The Runnable that will be executed.
+     * @param delayMillis The delay (in milliseconds) until the Runnable
+     *        will be executed.
+     *        
+     * @return Returns true if the Runnable was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the Runnable will be processed --
+     *         if the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     */
+    public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
+        return sendMessageDelayed(getPostMessage(r), delayMillis);
+    }
+    
+    /** @hide */
+    public final boolean postDelayed(Runnable r, int what, long delayMillis) {
+        return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
+    }
+
+    /**
+     * Causes the Runnable r to be added to the message queue, to be run
+     * after the specified amount of time elapses.
+     * The runnable will be run on the thread to which this handler
+     * is attached.
+     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+     * Time spent in deep sleep will add an additional delay to execution.
+     *
+     * @param r The Runnable that will be executed.
+     * @param token An instance which can be used to cancel {@code r} via
+     *         {@link #removeCallbacksAndMessages}.
+     * @param delayMillis The delay (in milliseconds) until the Runnable
+     *        will be executed.
+     *
+     * @return Returns true if the Runnable was successfully placed in to the
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the Runnable will be processed --
+     *         if the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     */
+    public final boolean postDelayed(
+            @NonNull Runnable r, @Nullable Object token, long delayMillis) {
+        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
+    }
+
+    /**
+     * Posts a message to an object that implements Runnable.
+     * Causes the Runnable r to executed on the next iteration through the
+     * message queue. The runnable will be run on the thread to which this
+     * handler is attached.
+     * <b>This method is only for use in very special circumstances -- it
+     * can easily starve the message queue, cause ordering problems, or have
+     * other unexpected side-effects.</b>
+     *  
+     * @param r The Runnable that will be executed.
+     * 
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+    public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
+        return sendMessageAtFrontOfQueue(getPostMessage(r));
+    }
+
+    /**
+     * Runs the specified task synchronously.
+     * <p>
+     * If the current thread is the same as the handler thread, then the runnable
+     * runs immediately without being enqueued.  Otherwise, posts the runnable
+     * to the handler and waits for it to complete before returning.
+     * </p><p>
+     * This method is dangerous!  Improper use can result in deadlocks.
+     * Never call this method while any locks are held or use it in a
+     * possibly re-entrant manner.
+     * </p><p>
+     * This method is occasionally useful in situations where a background thread
+     * must synchronously await completion of a task that must run on the
+     * handler's thread.  However, this problem is often a symptom of bad design.
+     * Consider improving the design (if possible) before resorting to this method.
+     * </p><p>
+     * One example of where you might want to use this method is when you just
+     * set up a Handler thread and need to perform some initialization steps on
+     * it before continuing execution.
+     * </p><p>
+     * If timeout occurs then this method returns <code>false</code> but the runnable
+     * will remain posted on the handler and may already be in progress or
+     * complete at a later time.
+     * </p><p>
+     * When using this method, be sure to use {@link Looper#quitSafely} when
+     * quitting the looper.  Otherwise {@link #runWithScissors} may hang indefinitely.
+     * (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
+     * </p>
+     *
+     * @param r The Runnable that will be executed synchronously.
+     * @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
+     *
+     * @return Returns true if the Runnable was successfully executed.
+     *         Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     *
+     * @hide This method is prone to abuse and should probably not be in the API.
+     * If we ever do make it part of the API, we might want to rename it to something
+     * less funny like runUnsafe().
+     */
+    public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
+        if (r == null) {
+            throw new IllegalArgumentException("runnable must not be null");
+        }
+        if (timeout < 0) {
+            throw new IllegalArgumentException("timeout must be non-negative");
+        }
+
+        if (Looper.myLooper() == mLooper) {
+            r.run();
+            return true;
+        }
+
+        BlockingRunnable br = new BlockingRunnable(r);
+        return br.postAndWait(this, timeout);
+    }
+
+    /**
+     * Remove any pending posts of Runnable r that are in the message queue.
+     */
+    public final void removeCallbacks(@NonNull Runnable r) {
+        mQueue.removeMessages(this, r, null);
+    }
+
+    /**
+     * Remove any pending posts of Runnable <var>r</var> with Object
+     * <var>token</var> that are in the message queue.  If <var>token</var> is null,
+     * all callbacks will be removed.
+     */
+    public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
+        mQueue.removeMessages(this, r, token);
+    }
+
+    /**
+     * Pushes a message onto the end of the message queue after all pending messages
+     * before the current time. It will be received in {@link #handleMessage},
+     * in the thread attached to this handler.
+     *  
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+    public final boolean sendMessage(@NonNull Message msg) {
+        return sendMessageDelayed(msg, 0);
+    }
+
+    /**
+     * Sends a Message containing only the what value.
+     *  
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+    public final boolean sendEmptyMessage(int what)
+    {
+        return sendEmptyMessageDelayed(what, 0);
+    }
+
+    /**
+     * Sends a Message containing only the what value, to be delivered
+     * after the specified amount of time elapses.
+     * @see #sendMessageDelayed(android.os.Message, long) 
+     * 
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        return sendMessageDelayed(msg, delayMillis);
+    }
+
+    /**
+     * Sends a Message containing only the what value, to be delivered 
+     * at a specific time.
+     * @see #sendMessageAtTime(android.os.Message, long)
+     *  
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+
+    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        return sendMessageAtTime(msg, uptimeMillis);
+    }
+
+    /**
+     * Enqueue a message into the message queue after all pending messages
+     * before (current time + delayMillis). You will receive it in
+     * {@link #handleMessage}, in the thread attached to this handler.
+     *  
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the message will be processed -- if
+     *         the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     */
+    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
+        if (delayMillis < 0) {
+            delayMillis = 0;
+        }
+        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
+    }
+
+    /**
+     * Enqueue a message into the message queue after all pending messages
+     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
+     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+     * Time spent in deep sleep will add an additional delay to execution.
+     * You will receive it in {@link #handleMessage}, in the thread attached
+     * to this handler.
+     * 
+     * @param uptimeMillis The absolute time at which the message should be
+     *         delivered, using the
+     *         {@link android.os.SystemClock#uptimeMillis} time-base.
+     *         
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the message will be processed -- if
+     *         the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     */
+    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
+        MessageQueue queue = mQueue;
+        if (queue == null) {
+            RuntimeException e = new RuntimeException(
+                    this + " sendMessageAtTime() called with no mQueue");
+            Log.w("Looper", e.getMessage(), e);
+            return false;
+        }
+        return enqueueMessage(queue, msg, uptimeMillis);
+    }
+
+    /**
+     * Enqueue a message at the front of the message queue, to be processed on
+     * the next iteration of the message loop.  You will receive it in
+     * {@link #handleMessage}, in the thread attached to this handler.
+     * <b>This method is only for use in very special circumstances -- it
+     * can easily starve the message queue, cause ordering problems, or have
+     * other unexpected side-effects.</b>
+     *  
+     * @return Returns true if the message was successfully placed in to the 
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
+        MessageQueue queue = mQueue;
+        if (queue == null) {
+            RuntimeException e = new RuntimeException(
+                this + " sendMessageAtTime() called with no mQueue");
+            Log.w("Looper", e.getMessage(), e);
+            return false;
+        }
+        return enqueueMessage(queue, msg, 0);
+    }
+
+    /**
+     * Executes the message synchronously if called on the same thread this handler corresponds to,
+     * or {@link #sendMessage pushes it to the queue} otherwise
+     *
+     * @return Returns true if the message was successfully ran or placed in to the
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     * @hide
+     */
+    public final boolean executeOrSendMessage(@NonNull Message msg) {
+        if (mLooper == Looper.myLooper()) {
+            dispatchMessage(msg);
+            return true;
+        }
+        return sendMessage(msg);
+    }
+
+    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
+            long uptimeMillis) {
+        msg.target = this;
+        msg.workSourceUid = ThreadLocalWorkSource.getUid();
+
+        if (mAsynchronous) {
+            msg.setAsynchronous(true);
+        }
+        return queue.enqueueMessage(msg, uptimeMillis);
+    }
+
+    private Object disallowNullArgumentIfShared(@Nullable Object arg) {
+        if (mIsShared && arg == null) {
+            throw new IllegalArgumentException("Null argument disallowed for shared handler."
+                    + " Consider creating your own Handler instance.");
+        }
+        return arg;
+    }
+
+    /**
+     * Remove any pending posts of messages with code 'what' that are in the
+     * message queue.
+     */
+    public final void removeMessages(int what) {
+        mQueue.removeMessages(this, what, null);
+    }
+
+    /**
+     * Remove any pending posts of messages with code 'what' and whose obj is
+     * 'object' that are in the message queue.  If <var>object</var> is null,
+     * all messages will be removed.
+     */
+    public final void removeMessages(int what, @Nullable Object object) {
+        mQueue.removeMessages(this, what, disallowNullArgumentIfShared(object));
+    }
+
+    /**
+     * Remove any pending posts of messages with code 'what' and whose obj is
+     * 'object' that are in the message queue.  If <var>object</var> is null,
+     * all messages will be removed.
+     * <p>
+     * Similar to {@link #removeMessages(int, Object)} but uses object equality
+     * ({@link Object#equals(Object)}) instead of reference equality (==) in
+     * determining whether object is the message's obj'.
+     *
+     *@hide
+     */
+    public final void removeEqualMessages(int what, @Nullable Object object) {
+        mQueue.removeEqualMessages(this, what, disallowNullArgumentIfShared(object));
+    }
+
+    /**
+     * Remove any pending posts of callbacks and sent messages whose
+     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
+     * all callbacks and messages will be removed.
+     */
+    public final void removeCallbacksAndMessages(@Nullable Object token) {
+        mQueue.removeCallbacksAndMessages(this, disallowNullArgumentIfShared(token));
+    }
+
+    /**
+     * Remove any pending posts of callbacks and sent messages whose
+     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
+     * all callbacks and messages will be removed.
+     *
+     *@hide
+     */
+    public final void removeCallbacksAndEqualMessages(@Nullable Object token) {
+        mQueue.removeCallbacksAndEqualMessages(this, disallowNullArgumentIfShared(token));
+    }
+    /**
+     * Check if there are any pending posts of messages with code 'what' in
+     * the message queue.
+     */
+    public final boolean hasMessages(int what) {
+        return mQueue.hasMessages(this, what, null);
+    }
+
+    /**
+     * Return whether there are any messages or callbacks currently scheduled on this handler.
+     * @hide
+     */
+    public final boolean hasMessagesOrCallbacks() {
+        return mQueue.hasMessages(this);
+    }
+
+    /**
+     * Check if there are any pending posts of messages with code 'what' and
+     * whose obj is 'object' in the message queue.
+     */
+    public final boolean hasMessages(int what, @Nullable Object object) {
+        return mQueue.hasMessages(this, what, object);
+    }
+
+    /**
+     * Check if there are any pending posts of messages with code 'what' and
+     * whose obj is 'object' in the message queue.
+     *
+     *@hide
+     */
+    public final boolean hasEqualMessages(int what, @Nullable Object object) {
+        return mQueue.hasEqualMessages(this, what, object);
+    }
+
+    /**
+     * Check if there are any pending posts of messages with callback r in
+     * the message queue.
+     */
+    public final boolean hasCallbacks(@NonNull Runnable r) {
+        return mQueue.hasMessages(this, r, null);
+    }
+
+    // if we can get rid of this method, the handler need not remember its loop
+    // we could instead export a getMessageQueue() method... 
+    @NonNull
+    public final Looper getLooper() {
+        return mLooper;
+    }
+
+    public final void dump(@NonNull Printer pw, @NonNull String prefix) {
+        pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
+        if (mLooper == null) {
+            pw.println(prefix + "looper uninitialized");
+        } else {
+            mLooper.dump(pw, prefix + "  ");
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public final void dumpMine(@NonNull Printer pw, @NonNull String prefix) {
+        pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
+        if (mLooper == null) {
+            pw.println(prefix + "looper uninitialized");
+        } else {
+            mLooper.dump(pw, prefix + "  ", this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Handler (" + getClass().getName() + ") {"
+        + Integer.toHexString(System.identityHashCode(this))
+        + "}";
+    }
+
+    @UnsupportedAppUsage
+    final IMessenger getIMessenger() {
+        synchronized (mQueue) {
+            if (mMessenger != null) {
+                return mMessenger;
+            }
+            mMessenger = new MessengerImpl();
+            return mMessenger;
+        }
+    }
+
+    private final class MessengerImpl extends IMessenger.Stub {
+        public void send(Message msg) {
+            msg.sendingUid = Binder.getCallingUid();
+            Handler.this.sendMessage(msg);
+        }
+    }
+
+    private static Message getPostMessage(Runnable r) {
+        Message m = Message.obtain();
+        m.callback = r;
+        return m;
+    }
+
+    @UnsupportedAppUsage
+    private static Message getPostMessage(Runnable r, Object token) {
+        Message m = Message.obtain();
+        m.obj = token;
+        m.callback = r;
+        return m;
+    }
+
+    private static void handleCallback(Message message) {
+        message.callback.run();
+    }
+
+    @UnsupportedAppUsage
+    final Looper mLooper;
+    final MessageQueue mQueue;
+    @UnsupportedAppUsage
+    final Callback mCallback;
+    final boolean mAsynchronous;
+    @UnsupportedAppUsage
+    IMessenger mMessenger;
+
+    /** If it's a shared handler, we disallow certain dangeraous operations. */
+    private final boolean mIsShared;
+
+    private static final class BlockingRunnable implements Runnable {
+        private final Runnable mTask;
+        private boolean mDone;
+
+        public BlockingRunnable(Runnable task) {
+            mTask = task;
+        }
+
+        @Override
+        public void run() {
+            try {
+                mTask.run();
+            } finally {
+                synchronized (this) {
+                    mDone = true;
+                    notifyAll();
+                }
+            }
+        }
+
+        public boolean postAndWait(Handler handler, long timeout) {
+            if (!handler.post(this)) {
+                return false;
+            }
+
+            synchronized (this) {
+                if (timeout > 0) {
+                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
+                    while (!mDone) {
+                        long delay = expirationTime - SystemClock.uptimeMillis();
+                        if (delay <= 0) {
+                            return false; // timeout
+                        }
+                        try {
+                            wait(delay);
+                        } catch (InterruptedException ex) {
+                        }
+                    }
+                } else {
+                    while (!mDone) {
+                        try {
+                            wait();
+                        } catch (InterruptedException ex) {
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+    }
+}
diff --git a/android-34/android/os/HandlerExecutor.java b/android-34/android/os/HandlerExecutor.java
new file mode 100644
index 0000000..416b24b
--- /dev/null
+++ b/android-34/android/os/HandlerExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * An adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+public class HandlerExecutor implements Executor {
+    private final Handler mHandler;
+
+    public HandlerExecutor(@NonNull Handler handler) {
+        mHandler = Preconditions.checkNotNull(handler);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        if (!mHandler.post(command)) {
+            throw new RejectedExecutionException(mHandler + " is shutting down");
+        }
+    }
+}
diff --git a/android-34/android/os/HandlerThread.java b/android-34/android/os/HandlerThread.java
new file mode 100644
index 0000000..4dd797a
--- /dev/null
+++ b/android-34/android/os/HandlerThread.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * A {@link Thread} that has a {@link Looper}.
+ * The {@link Looper} can then be used to create {@link Handler}s.
+ * <p>
+ * Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
+ */
+public class HandlerThread extends Thread {
+    int mPriority;
+    int mTid = -1;
+    Looper mLooper;
+    private @Nullable Handler mHandler;
+
+    public HandlerThread(String name) {
+        super(name);
+        mPriority = Process.THREAD_PRIORITY_DEFAULT;
+    }
+    
+    /**
+     * Constructs a HandlerThread.
+     * @param name
+     * @param priority The priority to run the thread at. The value supplied must be from 
+     * {@link android.os.Process} and not from java.lang.Thread.
+     */
+    public HandlerThread(String name, int priority) {
+        super(name);
+        mPriority = priority;
+    }
+    
+    /**
+     * Call back method that can be explicitly overridden if needed to execute some
+     * setup before Looper loops.
+     */
+    protected void onLooperPrepared() {
+    }
+
+    @Override
+    public void run() {
+        mTid = Process.myTid();
+        Looper.prepare();
+        synchronized (this) {
+            mLooper = Looper.myLooper();
+            notifyAll();
+        }
+        Process.setThreadPriority(mPriority);
+        onLooperPrepared();
+        Looper.loop();
+        mTid = -1;
+    }
+    
+    /**
+     * This method returns the Looper associated with this thread. If this thread not been started
+     * or for any reason isAlive() returns false, this method will return null. If this thread
+     * has been started, this method will block until the looper has been initialized.
+     * @return The looper.
+     */
+    public Looper getLooper() {
+        if (!isAlive()) {
+            return null;
+        }
+
+        boolean wasInterrupted = false;
+
+        // If the thread has been started, wait until the looper has been created.
+        synchronized (this) {
+            while (isAlive() && mLooper == null) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    wasInterrupted = true;
+                }
+            }
+        }
+
+        /*
+         * We may need to restore the thread's interrupted flag, because it may
+         * have been cleared above since we eat InterruptedExceptions
+         */
+        if (wasInterrupted) {
+            Thread.currentThread().interrupt();
+        }
+
+        return mLooper;
+    }
+
+    /**
+     * @return a shared {@link Handler} associated with this thread
+     * @hide
+     */
+    @NonNull
+    public Handler getThreadHandler() {
+        if (mHandler == null) {
+            mHandler = new Handler(getLooper());
+        }
+        return mHandler;
+    }
+
+    /**
+     * Quits the handler thread's looper.
+     * <p>
+     * Causes the handler thread's looper to terminate without processing any
+     * more messages in the message queue.
+     * </p><p>
+     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+     * For example, the {@link Handler#sendMessage(Message)} method will return false.
+     * </p><p class="note">
+     * Using this method may be unsafe because some messages may not be delivered
+     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
+     * that all pending work is completed in an orderly manner.
+     * </p>
+     *
+     * @return True if the looper looper has been asked to quit or false if the
+     * thread had not yet started running.
+     *
+     * @see #quitSafely
+     */
+    public boolean quit() {
+        Looper looper = getLooper();
+        if (looper != null) {
+            looper.quit();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Quits the handler thread's looper safely.
+     * <p>
+     * Causes the handler thread's looper to terminate as soon as all remaining messages
+     * in the message queue that are already due to be delivered have been handled.
+     * Pending delayed messages with due times in the future will not be delivered.
+     * </p><p>
+     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+     * For example, the {@link Handler#sendMessage(Message)} method will return false.
+     * </p><p>
+     * If the thread has not been started or has finished (that is if
+     * {@link #getLooper} returns null), then false is returned.
+     * Otherwise the looper is asked to quit and true is returned.
+     * </p>
+     *
+     * @return True if the looper looper has been asked to quit or false if the
+     * thread had not yet started running.
+     */
+    public boolean quitSafely() {
+        Looper looper = getLooper();
+        if (looper != null) {
+            looper.quitSafely();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the identifier of this thread. See Process.myTid().
+     */
+    public int getThreadId() {
+        return mTid;
+    }
+}
diff --git a/android-34/android/os/HardwarePropertiesManager.java b/android-34/android/os/HardwarePropertiesManager.java
new file mode 100644
index 0000000..3d072c5
--- /dev/null
+++ b/android-34/android/os/HardwarePropertiesManager.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.hardware.thermal.V1_0.Constants;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The HardwarePropertiesManager class provides a mechanism of accessing hardware state of a
+ * device: CPU, GPU and battery temperatures, CPU usage per core, fan speed, etc.
+ */
+@SystemService(Context.HARDWARE_PROPERTIES_SERVICE)
+public class HardwarePropertiesManager {
+
+    private static final String TAG = HardwarePropertiesManager.class.getSimpleName();
+
+    private final IHardwarePropertiesManager mService;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "DEVICE_TEMPERATURE_" }, value = {
+            DEVICE_TEMPERATURE_CPU,
+            DEVICE_TEMPERATURE_GPU,
+            DEVICE_TEMPERATURE_BATTERY,
+            DEVICE_TEMPERATURE_SKIN
+    })
+    public @interface DeviceTemperatureType {}
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "TEMPERATURE_" }, value = {
+            TEMPERATURE_CURRENT,
+            TEMPERATURE_THROTTLING,
+            TEMPERATURE_SHUTDOWN,
+            TEMPERATURE_THROTTLING_BELOW_VR_MIN
+    })
+    public @interface TemperatureSource {}
+
+    /**
+     * Device temperature types.
+     */
+    // These constants are also defined in android/os/enums.proto.
+    // Any change to the types here or in the thermal hal should be made in the proto as well.
+    /** Temperature of CPUs in Celsius. */
+    public static final int DEVICE_TEMPERATURE_CPU = Constants.TemperatureType.CPU;
+
+    /** Temperature of GPUs in Celsius. */
+    public static final int DEVICE_TEMPERATURE_GPU = Constants.TemperatureType.GPU;
+
+    /** Temperature of battery in Celsius. */
+    public static final int DEVICE_TEMPERATURE_BATTERY = Constants.TemperatureType.BATTERY;
+
+    /** Temperature of device skin in Celsius. */
+    public static final int DEVICE_TEMPERATURE_SKIN = Constants.TemperatureType.SKIN;
+
+    /** Get current temperature. */
+    public static final int TEMPERATURE_CURRENT = 0;
+
+    /** Get throttling temperature threshold. */
+    public static final int TEMPERATURE_THROTTLING = 1;
+
+    /** Get shutdown temperature threshold. */
+    public static final int TEMPERATURE_SHUTDOWN = 2;
+
+    /**
+     * Get throttling temperature threshold above which minimum clockrates for VR mode will not be
+     * met.
+     */
+    public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3;
+
+    /** Undefined temperature constant. */
+    public static final float UNDEFINED_TEMPERATURE = -Float.MAX_VALUE;
+
+    /** Calling app context. */
+    private final Context mContext;
+
+    /** @hide */
+    public HardwarePropertiesManager(Context context, IHardwarePropertiesManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Return an array of device temperatures in Celsius.
+     *
+     * @param type type of requested device temperature, one of {@link #DEVICE_TEMPERATURE_CPU},
+     * {@link #DEVICE_TEMPERATURE_GPU}, {@link #DEVICE_TEMPERATURE_BATTERY} or {@link
+     * #DEVICE_TEMPERATURE_SKIN}.
+     * @param source source of requested device temperature, one of {@link #TEMPERATURE_CURRENT},
+     * {@link #TEMPERATURE_THROTTLING}, {@link #TEMPERATURE_THROTTLING_BELOW_VR_MIN} or
+     * {@link #TEMPERATURE_SHUTDOWN}.
+     * @return an array of requested float device temperatures. Temperature equals to
+     *         {@link #UNDEFINED_TEMPERATURE} if undefined.
+     *         Empty if platform doesn't provide the queried temperature.
+     *
+     * @throws SecurityException if something other than the device owner or the current VR service
+     *         tries to retrieve information provided by this service.
+    */
+    public @NonNull float[] getDeviceTemperatures(@DeviceTemperatureType int type,
+            @TemperatureSource int source) {
+        switch (type) {
+            case DEVICE_TEMPERATURE_CPU:
+            case DEVICE_TEMPERATURE_GPU:
+            case DEVICE_TEMPERATURE_BATTERY:
+            case DEVICE_TEMPERATURE_SKIN:
+                switch (source) {
+                    case TEMPERATURE_CURRENT:
+                    case TEMPERATURE_THROTTLING:
+                    case TEMPERATURE_SHUTDOWN:
+                    case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
+                        try {
+                            return mService.getDeviceTemperatures(mContext.getOpPackageName(), type,
+                                    source);
+                        } catch (RemoteException e) {
+                            throw e.rethrowFromSystemServer();
+                        }
+                    default:
+                        Log.w(TAG, "Unknown device temperature source.");
+                        return new float[0];
+                }
+            default:
+                Log.w(TAG, "Unknown device temperature type.");
+                return new float[0];
+        }
+    }
+
+    /**
+     * Return an array of CPU usage info for each core.
+     *
+     * @return an array of {@link android.os.CpuUsageInfo} for each core. Return {@code null} for
+     *         each unplugged core.
+     *         Empty if CPU usage is not supported on this system.
+     *
+     * @throws SecurityException if something other than the device owner or the current VR service
+     *         tries to retrieve information provided by this service.
+     */
+    public @NonNull CpuUsageInfo[] getCpuUsages() {
+        try {
+            return mService.getCpuUsages(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return an array of fan speeds in RPM.
+     *
+     * @return an array of float fan speeds in RPM. Empty if there are no fans or fan speed is not
+     * supported on this system.
+     *
+     * @throws SecurityException if something other than the device owner or the current VR service
+     *         tries to retrieve information provided by this service.
+     */
+    public @NonNull float[] getFanSpeeds() {
+        try {
+            return mService.getFanSpeeds(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-34/android/os/HidlMemory.java b/android-34/android/os/HidlMemory.java
new file mode 100644
index 0000000..2539a6b
--- /dev/null
+++ b/android-34/android/os/HidlMemory.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * An abstract representation of a memory block, as representing by the HIDL system.
+ *
+ * The block is defined by a {name, size, handle} tuple, where the name is used to determine how to
+ * interpret the handle. The underlying handle is assumed to be owned by this instance and will be
+ * closed as soon as {@link #close()} is called on this instance, or this instance has been
+ * finalized (the latter supports using it in a shared manner without having to worry about who owns
+ * this instance, the former is more efficient resource-wise and is recommended for most use-cases).
+ * Note, however, that ownership of the handle does not necessarily imply ownership of the
+ * underlying file descriptors - the underlying handle may or may not own them. If you want the
+ * underlying handle to outlive this instance, call {@link #releaseHandle()} to obtain the handle
+ * and detach the ownership relationship.
+ *
+ * @hide
+ */
+@SystemApi
+public class HidlMemory implements Closeable {
+    private final @NonNull String mName;
+    private final long mSize;
+    private @Nullable NativeHandle mHandle;
+    private long mNativeContext;  // For use of native code.
+
+    /**
+     * Constructor.
+     *
+     * @param name      The name of the IMapper service used to resolve the handle (e.g. "ashmem").
+     * @param size      The (non-negative) size in bytes of the memory block.
+     * @param handle    The handle. May be null. This instance will own the handle and will close it
+     *                  as soon as {@link #close()} is called or the object is destroyed. This, this
+     *                  handle instance should generally not be shared with other clients.
+     */
+    public HidlMemory(@NonNull String name, @IntRange(from = 0) long size,
+            @Nullable NativeHandle handle) {
+        mName = name;
+        mSize = size;
+        mHandle = handle;
+    }
+
+    /**
+     * Create a copy of this instance, where the underlying handle (and its file descriptors) have
+     * been duplicated.
+     */
+    @NonNull
+    public HidlMemory dup() throws IOException {
+        return new HidlMemory(mName, mSize, mHandle != null ? mHandle.dup() : null);
+    }
+
+    /**
+     * Close the underlying native handle. No-op if handle is null or has been released using {@link
+     * #releaseHandle()}.
+     */
+    @Override
+    public void close() throws IOException {
+        if (mHandle != null) {
+            mHandle.close();
+            mHandle = null;
+        }
+    }
+
+    /**
+     * Disowns the underlying handle and returns it. The underlying handle becomes null.
+     *
+     * @return The underlying handle.
+     */
+    @Nullable
+    public NativeHandle releaseHandle() {
+        NativeHandle handle = mHandle;
+        mHandle = null;
+        return handle;
+    }
+
+    /**
+     * Gets the name, which represents how the handle is to be interpreted.
+     *
+     * @return The name.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Gets the size of the block, in bytes.
+     *
+     * @return The size.
+     */
+    public long getSize() {
+        return mSize;
+    }
+
+    /**
+     * Gets a native handle. The actual interpretation depends on the name and is implementation
+     * defined.
+     *
+     * @return The native handle.
+     */
+    @Nullable
+    public NativeHandle getHandle() {
+        return mHandle;
+    }
+
+    @Override
+    protected void finalize() {
+        try {
+            close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            nativeFinalize();
+        }
+    }
+
+    private native void nativeFinalize();
+}
diff --git a/android-34/android/os/HidlMemoryUtil.java b/android-34/android/os/HidlMemoryUtil.java
new file mode 100644
index 0000000..a1b2aef
--- /dev/null
+++ b/android-34/android/os/HidlMemoryUtil.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import static android.system.OsConstants.MAP_SHARED;
+import static android.system.OsConstants.PROT_READ;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.nio.DirectByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides utilities for dealing with HidlMemory.
+ *
+ * @hide
+ */
+public final class HidlMemoryUtil {
+    static private final String TAG = "HidlMemoryUtil";
+
+    private HidlMemoryUtil() {
+    }
+
+    /**
+     * Copies a byte-array into a new Ashmem region and return it as HidlMemory.
+     * The returned instance owns the underlying file descriptors, and the client should generally
+     * call close on it when no longer in use (or otherwise, when the object gets destroyed it will
+     * be closed).
+     *
+     * @param input The input byte array.
+     * @return A HidlMemory instance, containing a copy of the input.
+     */
+    public static @NonNull
+    HidlMemory byteArrayToHidlMemory(@NonNull byte[] input) {
+        return byteArrayToHidlMemory(input, null);
+    }
+
+    /**
+     * Copies a byte-array into a new Ashmem region and return it as HidlMemory.
+     * The returned instance owns the underlying file descriptors, and the client should generally
+     * call close on it when no longer in use (or otherwise, when the object gets destroyed it will
+     * be closed).
+     *
+     * @param input The input byte array.
+     * @param name  An optional name for the ashmem region.
+     * @return A HidlMemory instance, containing a copy of the input.
+     */
+    public static @NonNull
+    HidlMemory byteArrayToHidlMemory(@NonNull byte[] input, @Nullable String name) {
+        Preconditions.checkNotNull(input);
+
+        if (input.length == 0) {
+            return new HidlMemory("ashmem", 0, null);
+        }
+
+        try (SharedMemory shmem = SharedMemory.create(name != null ? name : "", input.length)) {
+            ByteBuffer buffer = shmem.mapReadWrite();
+            buffer.put(input);
+            shmem.unmap(buffer);
+            return sharedMemoryToHidlMemory(shmem);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Copies a byte list into a new Ashmem region and return it as HidlMemory.
+     * The returned instance owns the underlying file descriptors, and the client should generally
+     * call close on it when no longer in use (or otherwise, when the object gets destroyed it will
+     * be closed).
+     *
+     * @param input The input byte list.
+     * @return A HidlMemory instance, containing a copy of the input.
+     */
+    public static @NonNull
+    HidlMemory byteListToHidlMemory(@NonNull List<Byte> input) {
+        return byteListToHidlMemory(input, null);
+    }
+
+    /**
+     * Copies a byte list into a new Ashmem region and return it as HidlMemory.
+     * The returned instance owns the underlying file descriptors, and the client should generally
+     * call close on it when no longer in use (or otherwise, when the object gets destroyed it will
+     * be closed).
+     *
+     * @param input The input byte list.
+     * @param name  An optional name for the ashmem region.
+     * @return A HidlMemory instance, containing a copy of the input.
+     */
+    public static @NonNull
+    HidlMemory byteListToHidlMemory(@NonNull List<Byte> input, @Nullable String name) {
+        Preconditions.checkNotNull(input);
+
+        if (input.isEmpty()) {
+            return new HidlMemory("ashmem", 0, null);
+        }
+
+        try (SharedMemory shmem = SharedMemory.create(name != null ? name : "", input.size())) {
+            ByteBuffer buffer = shmem.mapReadWrite();
+            for (Byte b : input) {
+                buffer.put(b);
+            }
+            shmem.unmap(buffer);
+            return sharedMemoryToHidlMemory(shmem);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Copies all data from a HidlMemory instance into a byte array.
+     *
+     * @param mem The HidlMemory instance. Must be of name "ashmem" and of size that doesn't exceed
+     *            {@link Integer#MAX_VALUE}.
+     * @return A byte array, containing a copy of the input.
+     */
+    public static @NonNull
+    byte[] hidlMemoryToByteArray(@NonNull HidlMemory mem) {
+        Preconditions.checkNotNull(mem);
+        Preconditions.checkArgumentInRange(mem.getSize(), 0L, (long) Integer.MAX_VALUE,
+                "Memory size");
+        Preconditions.checkArgument(mem.getSize() == 0 || mem.getName().equals("ashmem"),
+                "Unsupported memory type: %s", mem.getName());
+
+        if (mem.getSize() == 0) {
+            return new byte[0];
+        }
+
+        ByteBuffer buffer = getBuffer(mem);
+        byte[] result = new byte[buffer.remaining()];
+        buffer.get(result);
+        return result;
+    }
+
+    /**
+     * Copies all data from a HidlMemory instance into a byte list.
+     *
+     * @param mem The HidlMemory instance. Must be of name "ashmem" and of size that doesn't exceed
+     *            {@link Integer#MAX_VALUE}.
+     * @return A byte list, containing a copy of the input.
+     */
+    @SuppressLint("ConcreteCollection")
+    public static @NonNull
+    ArrayList<Byte> hidlMemoryToByteList(@NonNull HidlMemory mem) {
+        Preconditions.checkNotNull(mem);
+        Preconditions.checkArgumentInRange(mem.getSize(), 0L, (long) Integer.MAX_VALUE,
+                "Memory size");
+        Preconditions.checkArgument(mem.getSize() == 0 || mem.getName().equals("ashmem"),
+                "Unsupported memory type: %s", mem.getName());
+
+        if (mem.getSize() == 0) {
+            return new ArrayList<>();
+        }
+
+        ByteBuffer buffer = getBuffer(mem);
+
+        ArrayList<Byte> result = new ArrayList<>(buffer.remaining());
+        while (buffer.hasRemaining()) {
+            result.add(buffer.get());
+        }
+        return result;
+    }
+
+    /**
+     * Converts a SharedMemory to a HidlMemory without copying.
+     *
+     * @param shmem The shared memory object. Null means "empty" and will still result in a non-null
+     *              return value.
+     * @return The HidlMemory instance.
+     */
+    @NonNull public static HidlMemory sharedMemoryToHidlMemory(@Nullable SharedMemory shmem) {
+        if (shmem == null) {
+            return new HidlMemory("ashmem", 0, null);
+        }
+        return fileDescriptorToHidlMemory(shmem.getFileDescriptor(), shmem.getSize());
+    }
+
+    /**
+     * Converts a FileDescriptor to a HidlMemory without copying.
+     *
+     * @param fd   The FileDescriptor object. Null is allowed if size is 0 and will still result in
+     *             a non-null return value.
+     * @param size The size of the memory buffer.
+     * @return The HidlMemory instance.
+     */
+    @NonNull public static HidlMemory fileDescriptorToHidlMemory(@Nullable FileDescriptor fd,
+            int size) {
+        Preconditions.checkArgument(fd != null || size == 0);
+        if (fd == null) {
+            return new HidlMemory("ashmem", 0, null);
+        }
+        try {
+            NativeHandle handle = new NativeHandle(Os.dup(fd), true);
+            return new HidlMemory("ashmem", size, handle);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static ByteBuffer getBuffer(@NonNull HidlMemory mem) {
+        try {
+            final int size = (int) mem.getSize();
+
+            if (size == 0) {
+                return ByteBuffer.wrap(new byte[0]);
+            }
+
+            NativeHandle handle = mem.getHandle();
+
+            final long address = Os.mmap(0, size, PROT_READ, MAP_SHARED, handle.getFileDescriptor(),
+                    0);
+            return new DirectByteBuffer(size, address, handle.getFileDescriptor(), () -> {
+                try {
+                    Os.munmap(address, size);
+                } catch (ErrnoException e) {
+                    Log.wtf(TAG, e);
+                }
+            }, true);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/android-34/android/os/HidlSupport.java b/android-34/android/os/HidlSupport.java
new file mode 100644
index 0000000..91b796a
--- /dev/null
+++ b/android-34/android/os/HidlSupport.java
@@ -0,0 +1,223 @@
+/*
+ * 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.os;
+
+import android.annotation.SystemApi;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+/** @hide */
+@SystemApi
+public class HidlSupport {
+    /**
+     * Similar to Objects.deepEquals, but also take care of lists.
+     * Two objects of HIDL types are considered equal if:
+     * 1. Both null
+     * 2. Both non-null, and of the same class, and:
+     * 2.1 Both are primitive arrays / enum arrays, elements are equal using == check
+     * 2.2 Both are object arrays, elements are checked recursively
+     * 2.3 Both are Lists, elements are checked recursively
+     * 2.4 (If both are collections other than lists or maps, throw an error)
+     * 2.5 lft.equals(rgt) returns true
+     * @hide
+     */
+    @SystemApi
+    public static boolean deepEquals(Object lft, Object rgt) {
+        if (lft == rgt) {
+            return true;
+        }
+        if (lft == null || rgt == null) {
+            return false;
+        }
+
+        Class<?> lftClazz = lft.getClass();
+        Class<?> rgtClazz = rgt.getClass();
+        if (lftClazz != rgtClazz) {
+            return false;
+        }
+
+        if (lftClazz.isArray()) {
+            Class<?> lftElementType = lftClazz.getComponentType();
+            if (lftElementType != rgtClazz.getComponentType()) {
+                return false;
+            }
+
+            if (lftElementType.isPrimitive()) {
+                return Objects.deepEquals(lft, rgt);
+            }
+
+            Object[] lftArray = (Object[])lft;
+            Object[] rgtArray = (Object[])rgt;
+            return (lftArray.length == rgtArray.length) &&
+                   IntStream.range(0, lftArray.length).allMatch(
+                        i -> deepEquals(lftArray[i], rgtArray[i]));
+        }
+
+        if (lft instanceof List<?>) {
+            List<Object> lftList = (List<Object>)lft;
+            List<Object> rgtList = (List<Object>)rgt;
+            if (lftList.size() != rgtList.size()) {
+                return false;
+            }
+
+            Iterator<Object> lftIter = lftList.iterator();
+            return rgtList.stream()
+                    .allMatch(rgtElement -> deepEquals(lftIter.next(), rgtElement));
+        }
+
+        throwErrorIfUnsupportedType(lft);
+
+        return lft.equals(rgt);
+    }
+
+    /**
+     * Class which can be used to fetch an object out of a lambda. Fetching an object
+     * out of a local scope with HIDL is a common operation (although usually it can
+     * and should be avoided).
+     *
+     * @param <E> Inner object type.
+     * @hide
+     */
+    public static final class Mutable<E> {
+        public E value;
+
+        public Mutable() {
+            value = null;
+        }
+
+        public Mutable(E value) {
+            this.value = value;
+        }
+    }
+
+    /**
+     * Similar to Arrays.deepHashCode, but also take care of lists.
+     * @hide
+     */
+    @SystemApi
+    public static int deepHashCode(Object o) {
+        if (o == null) {
+            return 0;
+        }
+        Class<?> clazz = o.getClass();
+        if (clazz.isArray()) {
+            Class<?> elementType = clazz.getComponentType();
+            if (elementType.isPrimitive()) {
+                return primitiveArrayHashCode(o);
+            }
+            return Arrays.hashCode(Arrays.stream((Object[])o)
+                    .mapToInt(element -> deepHashCode(element))
+                    .toArray());
+        }
+
+        if (o instanceof List<?>) {
+            return Arrays.hashCode(((List<Object>)o).stream()
+                    .mapToInt(element -> deepHashCode(element))
+                    .toArray());
+        }
+
+        throwErrorIfUnsupportedType(o);
+
+        return o.hashCode();
+    }
+
+    /** @hide */
+    private static void throwErrorIfUnsupportedType(Object o) {
+        if (o instanceof Collection<?> && !(o instanceof List<?>)) {
+            throw new UnsupportedOperationException(
+                    "Cannot check equality on collections other than lists: " +
+                    o.getClass().getName());
+        }
+
+        if (o instanceof Map<?, ?>) {
+            throw new UnsupportedOperationException(
+                    "Cannot check equality on maps");
+        }
+    }
+
+    /** @hide */
+    private static int primitiveArrayHashCode(Object o) {
+        Class<?> elementType = o.getClass().getComponentType();
+        if (elementType == boolean.class) {
+            return Arrays.hashCode(((boolean[])o));
+        }
+        if (elementType == byte.class) {
+            return Arrays.hashCode(((byte[])o));
+        }
+        if (elementType == char.class) {
+            return Arrays.hashCode(((char[])o));
+        }
+        if (elementType == double.class) {
+            return Arrays.hashCode(((double[])o));
+        }
+        if (elementType == float.class) {
+            return Arrays.hashCode(((float[])o));
+        }
+        if (elementType == int.class) {
+            return Arrays.hashCode(((int[])o));
+        }
+        if (elementType == long.class) {
+            return Arrays.hashCode(((long[])o));
+        }
+        if (elementType == short.class) {
+            return Arrays.hashCode(((short[])o));
+        }
+        // Should not reach here.
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Test that two interfaces are equal. This is the Java equivalent to C++
+     * interfacesEqual function.
+     * This essentially calls .equals on the internal binder objects (via Binder()).
+     * - If both interfaces are proxies, asBinder() returns a {@link HwRemoteBinder}
+     *   object, and they are compared in {@link HwRemoteBinder#equals}.
+     * - If both interfaces are stubs, asBinder() returns the object itself. By default,
+     *   auto-generated IFoo.Stub does not override equals(), but an implementation can
+     *   optionally override it, and {@code interfacesEqual} will use it here.
+     * @hide
+     */
+    @SystemApi
+    public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
+        if (lft == rgt) {
+            return true;
+        }
+        if (lft == null || rgt == null) {
+            return false;
+        }
+        if (!(rgt instanceof IHwInterface)) {
+            return false;
+        }
+        return Objects.equals(lft.asBinder(), ((IHwInterface) rgt).asBinder());
+    }
+
+    /**
+     * Return PID of process only if on a non-user build. For debugging purposes.
+     * @hide
+     */
+    @SystemApi
+    public static native int getPidIfSharable();
+
+    /** @hide */
+    public HidlSupport() {}
+}
diff --git a/android-34/android/os/HwBinder.java b/android-34/android/os/HwBinder.java
new file mode 100644
index 0000000..feed208
--- /dev/null
+++ b/android-34/android/os/HwBinder.java
@@ -0,0 +1,166 @@
+/*
+ * 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.os;
+
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.NoSuchElementException;
+
+/** @hide */
+@SystemApi
+public abstract class HwBinder implements IHwBinder {
+    private static final String TAG = "HwBinder";
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    /**
+     * Create and initialize a HwBinder object and the native objects
+     * used to allow this to participate in hwbinder transactions.
+     */
+    public HwBinder() {
+        native_setup();
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    @Override
+    public final native void transact(
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
+
+    /**
+     * Process a hwbinder transaction.
+     *
+     * @param code interface specific code for interface.
+     * @param request parceled transaction
+     * @param reply object to parcel reply into
+     * @param flags transaction flags to be chosen by wire protocol
+     */
+    public abstract void onTransact(
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
+
+    /**
+     * Registers this service with the hwservicemanager.
+     *
+     * @param serviceName instance name of the service
+     */
+    public native final void registerService(String serviceName)
+        throws RemoteException;
+
+    /**
+     * Returns the specified service from the hwservicemanager. Does not retry.
+     *
+     * @param iface fully-qualified interface name for example [email protected]::IBaz
+     * @param serviceName the instance name of the service for example default.
+     * @throws NoSuchElementException when the service is unavailable
+     */
+    public static final IHwBinder getService(
+            String iface,
+            String serviceName)
+        throws RemoteException, NoSuchElementException {
+        return getService(iface, serviceName, false /* retry */);
+    }
+    /**
+     * Returns the specified service from the hwservicemanager.
+     * @param iface fully-qualified interface name for example [email protected]::IBaz
+     * @param serviceName the instance name of the service for example default.
+     * @param retry whether to wait for the service to start if it's not already started
+     * @throws NoSuchElementException when the service is unavailable
+     */
+    public static native final IHwBinder getService(
+            String iface,
+            String serviceName,
+            boolean retry)
+        throws RemoteException, NoSuchElementException;
+
+    /**
+     * This allows getService to bypass the VINTF manifest for testing only.
+     *
+     * Disabled on user builds.
+     * @hide
+     */
+    public static native final void setTrebleTestingOverride(
+            boolean testingOverride);
+
+    /**
+     * Configures how many threads the process-wide hwbinder threadpool
+     * has to process incoming requests.
+     *
+     * @param maxThreads total number of threads to create (includes this thread if
+     *     callerWillJoin is true)
+     * @param callerWillJoin whether joinRpcThreadpool will be called in advance
+     */
+    public static native final void configureRpcThreadpool(
+            long maxThreads, boolean callerWillJoin);
+
+    /**
+     * Current thread will join hwbinder threadpool and process
+     * commands in the pool. Should be called after configuring
+     * a threadpool with callerWillJoin true and then registering
+     * the provided service if this thread doesn't need to do
+     * anything else.
+     */
+    public static native final void joinRpcThreadpool();
+
+    // Returns address of the "freeFunction".
+    private static native final long native_init();
+
+    private native final void native_setup();
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwBinder.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private long mNativeContext;
+
+    private static native void native_report_sysprop_change();
+
+    /**
+     * Enable instrumentation if available.
+     *
+     * On a non-user build, this method:
+     * - tries to enable atracing (if enabled)
+     * - tries to enable coverage dumps (if running in VTS)
+     * - tries to enable record and replay (if running in VTS)
+     */
+    public static void enableInstrumentation() {
+        native_report_sysprop_change();
+    }
+
+    /**
+     * Notifies listeners that a system property has changed
+     *
+     * TODO(b/72480743): remove this method
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+    public static void reportSyspropChanged() {
+        native_report_sysprop_change();
+    }
+}
diff --git a/android-34/android/os/HwBlob.java b/android-34/android/os/HwBlob.java
new file mode 100644
index 0000000..a43fbdb
--- /dev/null
+++ b/android-34/android/os/HwBlob.java
@@ -0,0 +1,461 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Represents fixed sized allocation of marshalled data used. Helper methods
+ * allow for access to the unmarshalled data in a variety of ways.
+ *
+ * @hide
+ */
+@SystemApi
+public class HwBlob {
+    private static final String TAG = "HwBlob";
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    public HwBlob(int size) {
+        native_setup(size);
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    /**
+     * @param offset offset to unmarshall a boolean from
+     * @return the unmarshalled boolean value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final boolean getBool(long offset);
+    /**
+     * @param offset offset to unmarshall a byte from
+     * @return the unmarshalled byte value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final byte getInt8(long offset);
+    /**
+     * @param offset offset to unmarshall a short from
+     * @return the unmarshalled short value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final short getInt16(long offset);
+    /**
+     * @param offset offset to unmarshall an int from
+     * @return the unmarshalled int value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final int getInt32(long offset);
+    /**
+     * @param offset offset to unmarshall a long from
+     * @return the unmarshalled long value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final long getInt64(long offset);
+    /**
+     * @param offset offset to unmarshall a float from
+     * @return the unmarshalled float value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final float getFloat(long offset);
+    /**
+     * @param offset offset to unmarshall a double from
+     * @return the unmarshalled double value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final double getDouble(long offset);
+    /**
+     * @param offset offset to unmarshall a string from
+     * @return the unmarshalled string value
+     * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+     */
+    public native final String getString(long offset);
+    /**
+     * For embedded fields that follow a two-step approach for reading, first obtain their field
+     * handle using this method, and pass that field handle to the respective
+     * HwParcel.readEmbedded*() method.
+     * @param offset The field offset.
+     * @return The field handle.
+     */
+    public native final long getFieldHandle(long offset);
+
+    /**
+     * Copy the blobs data starting from the given byte offset into the range, copying
+     * a total of size elements.
+     *
+     * @param offset starting location in blob
+     * @param array destination array
+     * @param size total number of elements to copy
+     * @throws IllegalArgumentException array.length < size
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jboolean)] out of the blob.
+     */
+    public native final void copyToBoolArray(long offset, boolean[] array, int size);
+    /**
+     * Copy the blobs data starting from the given byte offset into the range, copying
+     * a total of size elements.
+     *
+     * @param offset starting location in blob
+     * @param array destination array
+     * @param size total number of elements to copy
+     * @throws IllegalArgumentException array.length < size
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jbyte)] out of the blob.
+     */
+    public native final void copyToInt8Array(long offset, byte[] array, int size);
+    /**
+     * Copy the blobs data starting from the given byte offset into the range, copying
+     * a total of size elements.
+     *
+     * @param offset starting location in blob
+     * @param array destination array
+     * @param size total number of elements to copy
+     * @throws IllegalArgumentException array.length < size
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jshort)] out of the blob.
+     */
+    public native final void copyToInt16Array(long offset, short[] array, int size);
+    /**
+     * Copy the blobs data starting from the given byte offset into the range, copying
+     * a total of size elements.
+     *
+     * @param offset starting location in blob
+     * @param array destination array
+     * @param size total number of elements to copy
+     * @throws IllegalArgumentException array.length < size
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jint)] out of the blob.
+     */
+    public native final void copyToInt32Array(long offset, int[] array, int size);
+    /**
+     * Copy the blobs data starting from the given byte offset into the range, copying
+     * a total of size elements.
+     *
+     * @param offset starting location in blob
+     * @param array destination array
+     * @param size total number of elements to copy
+     * @throws IllegalArgumentException array.length < size
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jlong)] out of the blob.
+     */
+    public native final void copyToInt64Array(long offset, long[] array, int size);
+    /**
+     * Copy the blobs data starting from the given byte offset into the range, copying
+     * a total of size elements.
+     *
+     * @param offset starting location in blob
+     * @param array destination array
+     * @param size total number of elements to copy
+     * @throws IllegalArgumentException array.length < size
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jfloat)] out of the blob.
+     */
+    public native final void copyToFloatArray(long offset, float[] array, int size);
+    /**
+     * Copy the blobs data starting from the given byte offset into the range, copying
+     * a total of size elements.
+     *
+     * @param offset starting location in blob
+     * @param array destination array
+     * @param size total number of elements to copy
+     * @throws IllegalArgumentException array.length < size
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jdouble)] out of the blob.
+     */
+    public native final void copyToDoubleArray(long offset, double[] array, int size);
+
+    /**
+     * Writes a boolean value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jboolean)] is out of range
+     */
+    public native final void putBool(long offset, boolean x);
+    /**
+     * Writes a byte value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jbyte)] is out of range
+     */
+    public native final void putInt8(long offset, byte x);
+    /**
+     * Writes a short value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jshort)] is out of range
+     */
+    public native final void putInt16(long offset, short x);
+    /**
+     * Writes a int value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jint)] is out of range
+     */
+    public native final void putInt32(long offset, int x);
+    /**
+     * Writes a long value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jlong)] is out of range
+     */
+    public native final void putInt64(long offset, long x);
+    /**
+     * Writes a float value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jfloat)] is out of range
+     */
+    public native final void putFloat(long offset, float x);
+    /**
+     * Writes a double value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jdouble)] is out of range
+     */
+    public native final void putDouble(long offset, double x);
+    /**
+     * Writes a string value at an offset.
+     *
+     * @param offset location to write value
+     * @param x value to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jstring)] is out of range
+     */
+    public native final void putString(long offset, String x);
+    /**
+     * Writes a native handle (without duplicating the underlying file descriptors) at an offset.
+     *
+     * @param offset location to write value
+     * @param x a {@link NativeHandle} instance to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jobject)] is out of range
+     */
+    public native final void putNativeHandle(long offset, @Nullable NativeHandle x);
+
+    /**
+     * Put a boolean array contiguously at an offset in the blob.
+     *
+     * @param offset location to write values
+     * @param x array to write
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jboolean)] out of the blob.
+     */
+    public native final void putBoolArray(long offset, boolean[] x);
+    /**
+     * Put a byte array contiguously at an offset in the blob.
+     *
+     * @param offset location to write values
+     * @param x array to write
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jbyte)] out of the blob.
+     */
+    public native final void putInt8Array(long offset, byte[] x);
+    /**
+     * Put a short array contiguously at an offset in the blob.
+     *
+     * @param offset location to write values
+     * @param x array to write
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jshort)] out of the blob.
+     */
+    public native final void putInt16Array(long offset, short[] x);
+    /**
+     * Put a int array contiguously at an offset in the blob.
+     *
+     * @param offset location to write values
+     * @param x array to write
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jint)] out of the blob.
+     */
+    public native final void putInt32Array(long offset, int[] x);
+    /**
+     * Put a long array contiguously at an offset in the blob.
+     *
+     * @param offset location to write values
+     * @param x array to write
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jlong)] out of the blob.
+     */
+    public native final void putInt64Array(long offset, long[] x);
+    /**
+     * Put a float array contiguously at an offset in the blob.
+     *
+     * @param offset location to write values
+     * @param x array to write
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jfloat)] out of the blob.
+     */
+    public native final void putFloatArray(long offset, float[] x);
+    /**
+     * Put a double array contiguously at an offset in the blob.
+     *
+     * @param offset location to write values
+     * @param x array to write
+     * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jdouble)] out of the blob.
+     */
+    public native final void putDoubleArray(long offset, double[] x);
+
+    /**
+     * Write another HwBlob into this blob at the specified location.
+     *
+     * @param offset location to write value
+     * @param blob data to write
+     * @throws IndexOutOfBoundsException if [offset, offset + blob's size] outside of the range of
+     *     this blob.
+     */
+    public native final void putBlob(long offset, HwBlob blob);
+
+    /**
+     * Writes a HidlMemory instance (without duplicating the underlying file descriptors) at an
+     * offset.
+     *
+     * @param offset location to write value
+     * @param mem    a {@link HidlMemory} instance to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jobject)] is out of range
+     */
+    public final void putHidlMemory(long offset, @NonNull HidlMemory mem) {
+        putNativeHandle(offset + 0  /* offset of 'handle' field. */, mem.getHandle());
+        putInt64(offset + 16  /* offset of 'size' field. */, mem.getSize());
+        putString(offset + 24  /* offset of 'name' field. */, mem.getName());
+    }
+
+    /**
+     * @return current handle of HwBlob for reference in a parcelled binder transaction
+     */
+    public native final long handle();
+
+    /**
+     * Convert a primitive to a wrapped array for boolean.
+     *
+     * @param array from array
+     * @return transformed array
+     */
+    public static Boolean[] wrapArray(@NonNull boolean[] array) {
+        final int n = array.length;
+        Boolean[] wrappedArray = new Boolean[n];
+        for (int i = 0; i < n; ++i) {
+          wrappedArray[i] = array[i];
+        }
+        return wrappedArray;
+    }
+
+    /**
+     * Convert a primitive to a wrapped array for long.
+     *
+     * @param array from array
+     * @return transformed array
+     */
+    public static Long[] wrapArray(@NonNull long[] array) {
+        final int n = array.length;
+        Long[] wrappedArray = new Long[n];
+        for (int i = 0; i < n; ++i) {
+          wrappedArray[i] = array[i];
+        }
+        return wrappedArray;
+    }
+
+    /**
+     * Convert a primitive to a wrapped array for byte.
+     *
+     * @param array from array
+     * @return transformed array
+     */
+    public static Byte[] wrapArray(@NonNull byte[] array) {
+        final int n = array.length;
+        Byte[] wrappedArray = new Byte[n];
+        for (int i = 0; i < n; ++i) {
+          wrappedArray[i] = array[i];
+        }
+        return wrappedArray;
+    }
+
+    /**
+     * Convert a primitive to a wrapped array for short.
+     *
+     * @param array from array
+     * @return transformed array
+     */
+    public static Short[] wrapArray(@NonNull short[] array) {
+        final int n = array.length;
+        Short[] wrappedArray = new Short[n];
+        for (int i = 0; i < n; ++i) {
+          wrappedArray[i] = array[i];
+        }
+        return wrappedArray;
+    }
+
+    /**
+     * Convert a primitive to a wrapped array for int.
+     *
+     * @param array from array
+     * @return transformed array
+     */
+    public static Integer[] wrapArray(@NonNull int[] array) {
+        final int n = array.length;
+        Integer[] wrappedArray = new Integer[n];
+        for (int i = 0; i < n; ++i) {
+          wrappedArray[i] = array[i];
+        }
+        return wrappedArray;
+    }
+
+    /**
+     * Convert a primitive to a wrapped array for float.
+     *
+     * @param array from array
+     * @return transformed array
+     */
+    public static Float[] wrapArray(@NonNull float[] array) {
+        final int n = array.length;
+        Float[] wrappedArray = new Float[n];
+        for (int i = 0; i < n; ++i) {
+          wrappedArray[i] = array[i];
+        }
+        return wrappedArray;
+    }
+
+    /**
+     * Convert a primitive to a wrapped array for double.
+     *
+     * @param array from array
+     * @return transformed array
+     */
+    public static Double[] wrapArray(@NonNull double[] array) {
+        final int n = array.length;
+        Double[] wrappedArray = new Double[n];
+        for (int i = 0; i < n; ++i) {
+          wrappedArray[i] = array[i];
+        }
+        return wrappedArray;
+    }
+
+    // Returns address of the "freeFunction".
+    private static native final long native_init();
+
+    private native final void native_setup(int size);
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwBlob.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private long mNativeContext;
+}
+
+
diff --git a/android-34/android/os/HwParcel.java b/android-34/android/os/HwParcel.java
new file mode 100644
index 0000000..9fd37d4
--- /dev/null
+++ b/android-34/android/os/HwParcel.java
@@ -0,0 +1,703 @@
+/*
+ * 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/** @hide */
+@SystemApi
+public class HwParcel {
+    private static final String TAG = "HwParcel";
+
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+        STATUS_SUCCESS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Status {}
+
+    /**
+     * Success return error for a transaction. Written to parcels
+     * using writeStatus.
+     */
+    public static final int STATUS_SUCCESS      = 0;
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    @UnsupportedAppUsage
+    private HwParcel(boolean allocate) {
+        native_setup(allocate);
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    /**
+     * Creates an initialized and empty parcel.
+     */
+    public HwParcel() {
+        native_setup(true /* allocate */);
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    /**
+     * Writes an interface token into the parcel used to verify that
+     * a transaction has made it to the right type of interface.
+     *
+     * @param interfaceName fully qualified name of interface message
+     *     is being sent to.
+     */
+    @FastNative
+    public native final void writeInterfaceToken(String interfaceName);
+    /**
+     * Writes a boolean value to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeBool(boolean val);
+    /**
+     * Writes a byte value to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeInt8(byte val);
+    /**
+     * Writes a short value to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeInt16(short val);
+    /**
+     * Writes a int value to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeInt32(int val);
+    /**
+     * Writes a long value to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeInt64(long val);
+    /**
+     * Writes a float value to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeFloat(float val);
+    /**
+     * Writes a double value to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeDouble(double val);
+    /**
+     * Writes a String value to the end of the parcel.
+     *
+     * Note, this will be converted to UTF-8 when it is written.
+     *
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeString(String val);
+    /**
+     * Writes a native handle (without duplicating the underlying
+     * file descriptors) to the end of the parcel.
+     *
+     * @param val to write
+     */
+    @FastNative
+    public native final void writeNativeHandle(@Nullable NativeHandle val);
+
+    /**
+     * Writes an array of boolean values to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeBoolVector(boolean[] val);
+    /**
+     * Writes an array of byte values to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeInt8Vector(byte[] val);
+    /**
+     * Writes an array of short values to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeInt16Vector(short[] val);
+    /**
+     * Writes an array of int values to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeInt32Vector(int[] val);
+    /**
+     * Writes an array of long values to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeInt64Vector(long[] val);
+    /**
+     * Writes an array of float values to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeFloatVector(float[] val);
+    /**
+     * Writes an array of double values to the end of the parcel.
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeDoubleVector(double[] val);
+    /**
+     * Writes an array of String values to the end of the parcel.
+     *
+     * Note, these will be converted to UTF-8 as they are written.
+     *
+     * @param val to write
+     */
+    @FastNative
+    private native final void writeStringVector(String[] val);
+    /**
+     * Writes an array of native handles to the end of the parcel.
+     *
+     * Individual elements may be null but not the whole array.
+     *
+     * @param val array of {@link NativeHandle} objects to write
+     */
+    @FastNative
+    private native final void writeNativeHandleVector(NativeHandle[] val);
+
+    /**
+     * Helper method to write a list of Booleans to val.
+     * @param val list to write
+     */
+    public final void writeBoolVector(ArrayList<Boolean> val) {
+        final int n = val.size();
+        boolean[] array = new boolean[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeBoolVector(array);
+    }
+
+    /**
+     * Helper method to write a list of Booleans to the end of the parcel.
+     * @param val list to write
+     */
+    public final void writeInt8Vector(ArrayList<Byte> val) {
+        final int n = val.size();
+        byte[] array = new byte[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt8Vector(array);
+    }
+
+    /**
+     * Helper method to write a list of Shorts to the end of the parcel.
+     * @param val list to write
+     */
+    public final void writeInt16Vector(ArrayList<Short> val) {
+        final int n = val.size();
+        short[] array = new short[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt16Vector(array);
+    }
+
+    /**
+     * Helper method to write a list of Integers to the end of the parcel.
+     * @param val list to write
+     */
+    public final void writeInt32Vector(ArrayList<Integer> val) {
+        final int n = val.size();
+        int[] array = new int[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt32Vector(array);
+    }
+
+    /**
+     * Helper method to write a list of Longs to the end of the parcel.
+     * @param val list to write
+     */
+    public final void writeInt64Vector(ArrayList<Long> val) {
+        final int n = val.size();
+        long[] array = new long[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeInt64Vector(array);
+    }
+
+    /**
+     * Helper method to write a list of Floats to the end of the parcel.
+     * @param val list to write
+     */
+    public final void writeFloatVector(ArrayList<Float> val) {
+        final int n = val.size();
+        float[] array = new float[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeFloatVector(array);
+    }
+
+    /**
+     * Helper method to write a list of Doubles to the end of the parcel.
+     * @param val list to write
+     */
+    public final void writeDoubleVector(ArrayList<Double> val) {
+        final int n = val.size();
+        double[] array = new double[n];
+        for (int i = 0; i < n; ++i) {
+            array[i] = val.get(i);
+        }
+
+        writeDoubleVector(array);
+    }
+
+    /**
+     * Helper method to write a list of Strings to the end of the parcel.
+     * @param val list to write
+     */
+    public final void writeStringVector(ArrayList<String> val) {
+        writeStringVector(val.toArray(new String[val.size()]));
+    }
+
+    /**
+     * Helper method to write a list of native handles to the end of the parcel.
+     * @param val list of {@link NativeHandle} objects to write
+     */
+    public final void writeNativeHandleVector(@NonNull ArrayList<NativeHandle> val) {
+        writeNativeHandleVector(val.toArray(new NativeHandle[val.size()]));
+    }
+
+    /**
+     * Write a hwbinder object to the end of the parcel.
+     * @param binder value to write
+     */
+    @FastNative
+    public native final void writeStrongBinder(IHwBinder binder);
+
+    /**
+     * Write a HidlMemory object (without duplicating the underlying file descriptors) to the end
+     * of the parcel.
+     *
+     * @param memory value to write
+     */
+    @FastNative
+    public native final void writeHidlMemory(@NonNull HidlMemory memory);
+
+    /**
+     * Checks to make sure that the interface name matches the name written by the parcel
+     * sender by writeInterfaceToken
+     *
+     * @throws SecurityException interface doesn't match
+     */
+    public native final void enforceInterface(String interfaceName);
+
+    /**
+     * Reads a boolean value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final boolean readBool();
+    /**
+     * Reads a byte value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final byte readInt8();
+    /**
+     * Reads a short value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final short readInt16();
+    /**
+     * Reads a int value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final int readInt32();
+    /**
+     * Reads a long value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final long readInt64();
+    /**
+     * Reads a float value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final float readFloat();
+    /**
+     * Reads a double value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final double readDouble();
+    /**
+     * Reads a String value from the current location in the parcel.
+     * @return value parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final String readString();
+    /**
+     * Reads a native handle (without duplicating the underlying file
+     * descriptors) from the parcel. These file descriptors will only
+     * be open for the duration that the binder window is open. If they
+     * are needed further, you must call {@link NativeHandle#dup()}.
+     *
+     * @return a {@link NativeHandle} instance parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final @Nullable NativeHandle readNativeHandle();
+    /**
+     * Reads an embedded native handle (without duplicating the underlying
+     * file descriptors) from the parcel. These file descriptors will only
+     * be open for the duration that the binder window is open. If they
+     * are needed further, you must call {@link NativeHandle#dup()}. You
+     * do not need to call close on the NativeHandle returned from this.
+     *
+     * @param parentHandle handle from which to read the embedded object
+     * @param offset offset into parent
+     * @return a {@link NativeHandle} instance parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final @Nullable NativeHandle readEmbeddedNativeHandle(
+            long parentHandle, long offset);
+
+    /**
+     * Reads an array of boolean values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final boolean[] readBoolVectorAsArray();
+    /**
+     * Reads an array of byte values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final byte[] readInt8VectorAsArray();
+    /**
+     * Reads an array of short values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final short[] readInt16VectorAsArray();
+    /**
+     * Reads an array of int values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final int[] readInt32VectorAsArray();
+    /**
+     * Reads an array of long values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final long[] readInt64VectorAsArray();
+    /**
+     * Reads an array of float values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final float[] readFloatVectorAsArray();
+    /**
+     * Reads an array of double values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final double[] readDoubleVectorAsArray();
+    /**
+     * Reads an array of String values from the parcel.
+     * @return array of parsed values
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final String[] readStringVectorAsArray();
+    /**
+     * Reads an array of native handles from the parcel.
+     * @return array of {@link NativeHandle} objects
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    private native final NativeHandle[] readNativeHandleAsArray();
+
+    /**
+     * Convenience method to read a Boolean vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<Boolean> readBoolVector() {
+        Boolean[] array = HwBlob.wrapArray(readBoolVectorAsArray());
+
+        return new ArrayList<Boolean>(Arrays.asList(array));
+    }
+
+    /**
+     * Convenience method to read a Byte vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<Byte> readInt8Vector() {
+        Byte[] array = HwBlob.wrapArray(readInt8VectorAsArray());
+
+        return new ArrayList<Byte>(Arrays.asList(array));
+    }
+
+    /**
+     * Convenience method to read a Short vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<Short> readInt16Vector() {
+        Short[] array = HwBlob.wrapArray(readInt16VectorAsArray());
+
+        return new ArrayList<Short>(Arrays.asList(array));
+    }
+
+    /**
+     * Convenience method to read a Integer vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<Integer> readInt32Vector() {
+        Integer[] array = HwBlob.wrapArray(readInt32VectorAsArray());
+
+        return new ArrayList<Integer>(Arrays.asList(array));
+    }
+
+    /**
+     * Convenience method to read a Long vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<Long> readInt64Vector() {
+        Long[] array = HwBlob.wrapArray(readInt64VectorAsArray());
+
+        return new ArrayList<Long>(Arrays.asList(array));
+    }
+
+    /**
+     * Convenience method to read a Float vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<Float> readFloatVector() {
+        Float[] array = HwBlob.wrapArray(readFloatVectorAsArray());
+
+        return new ArrayList<Float>(Arrays.asList(array));
+    }
+
+    /**
+     * Convenience method to read a Double vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<Double> readDoubleVector() {
+        Double[] array = HwBlob.wrapArray(readDoubleVectorAsArray());
+
+        return new ArrayList<Double>(Arrays.asList(array));
+    }
+
+    /**
+     * Convenience method to read a String vector as an ArrayList.
+     * @return array of parsed values.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final ArrayList<String> readStringVector() {
+        return new ArrayList<String>(Arrays.asList(readStringVectorAsArray()));
+    }
+
+    /**
+     * Convenience method to read a vector of native handles as an ArrayList.
+     * @return array of {@link NativeHandle} objects.
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    public final @NonNull ArrayList<NativeHandle> readNativeHandleVector() {
+        return new ArrayList<NativeHandle>(Arrays.asList(readNativeHandleAsArray()));
+    }
+
+    /**
+     * Reads a strong binder value from the parcel.
+     * @return binder object read from parcel or null if no binder can be read
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final IHwBinder readStrongBinder();
+
+    /**
+     * Reads a HidlMemory value (without duplicating the underlying file
+     * descriptors) from the parcel. These file descriptors will only
+     * be open for the duration that the binder window is open. If they
+     * are needed further, you must call {@link HidlMemory#dup()}, which makes you also
+     * responsible for calling {@link HidlMemory#close()}.
+     *
+     * @return HidlMemory object read from parcel.
+     * @throws IllegalArgumentException if the parcel has no more data or is otherwise corrupt.
+     */
+    @FastNative
+    @NonNull
+    public native final HidlMemory readHidlMemory();
+
+    /**
+     * Reads an embedded HidlMemory (without duplicating the underlying
+     * file descriptors) from the parcel. These file descriptors will only
+     * be open for the duration that the binder window is open. If they
+     * are needed further, you must call {@link HidlMemory#dup()}. You
+     * do not need to call close on the HidlMemory returned from this.
+     *
+     * @param fieldHandle  handle of the field, obtained from the {@link HwBlob}.
+     * @param parentHandle parentHandle from which to read the embedded object
+     * @param offset       offset into parent
+     * @return a {@link HidlMemory} instance parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    @NonNull
+    public native final @Nullable
+    HidlMemory readEmbeddedHidlMemory(long fieldHandle, long parentHandle, long offset);
+
+    /**
+     * Read opaque segment of data as a blob.
+     * @return blob of size expectedSize
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    public native final HwBlob readBuffer(long expectedSize);
+
+    /**
+     * Read a buffer written using scatter gather.
+     *
+     * @param expectedSize size that buffer should be
+     * @param parentHandle handle from which to read the embedded buffer
+     * @param offset offset into parent
+     * @param nullable whether or not to allow for a null return
+     * @return blob of data with size expectedSize
+     * @throws NoSuchElementException if an embedded buffer is not available to read
+     * @throws IllegalArgumentException if expectedSize < 0
+     * @throws NullPointerException if the transaction specified the blob to be null
+     *    but nullable is false
+     */
+    @FastNative
+    public native final HwBlob readEmbeddedBuffer(
+            long expectedSize, long parentHandle, long offset,
+            boolean nullable);
+
+    /**
+     * Write a buffer into the transaction.
+     * @param blob blob to write into the parcel.
+     */
+    @FastNative
+    public native final void writeBuffer(HwBlob blob);
+    /**
+     * Write a status value into the blob.
+     * @param status value to write
+     */
+    @FastNative
+    public native final void writeStatus(int status);
+    /**
+     * @throws IllegalArgumentException if a success vaue cannot be read
+     * @throws RemoteException if success value indicates a transaction error
+     */
+    @FastNative
+    public native final void verifySuccess();
+    /**
+     * Should be called to reduce memory pressure when this object no longer needs
+     * to be written to.
+     */
+    @FastNative
+    public native final void releaseTemporaryStorage();
+    /**
+     * Should be called when object is no longer needed to reduce possible memory
+     * pressure if the Java GC does not get to this object in time.
+     */
+    @FastNative
+    public native final void release();
+
+    /**
+     * Sends the parcel to the specified destination.
+     */
+    public native final void send();
+
+    // Returns address of the "freeFunction".
+    private static native final long native_init();
+
+    @FastNative
+    private native final void native_setup(boolean allocate);
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwParcel.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private long mNativeContext;
+}
+
diff --git a/android-34/android/os/HwRemoteBinder.java b/android-34/android/os/HwRemoteBinder.java
new file mode 100644
index 0000000..756202e
--- /dev/null
+++ b/android-34/android/os/HwRemoteBinder.java
@@ -0,0 +1,74 @@
+/*
+ * 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import libcore.util.NativeAllocationRegistry;
+
+/** @hide */
+public class HwRemoteBinder implements IHwBinder {
+    private static final String TAG = "HwRemoteBinder";
+
+    private static final NativeAllocationRegistry sNativeRegistry;
+
+    @UnsupportedAppUsage
+    public HwRemoteBinder() {
+        native_setup_empty();
+
+        sNativeRegistry.registerNativeAllocation(
+                this,
+                mNativeContext);
+    }
+
+    @Override
+    public IHwInterface queryLocalInterface(String descriptor) {
+        return null;
+    }
+
+    @Override
+    public native final void transact(
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
+
+    public native boolean linkToDeath(DeathRecipient recipient, long cookie);
+    public native boolean unlinkToDeath(DeathRecipient recipient);
+
+    private static native final long native_init();
+
+    private native final void native_setup_empty();
+
+    static {
+        long freeFunction = native_init();
+
+        sNativeRegistry = new NativeAllocationRegistry(
+                HwRemoteBinder.class.getClassLoader(),
+                freeFunction,
+                128 /* size */);
+    }
+
+    private static final void sendDeathNotice(DeathRecipient recipient, long cookie) {
+        recipient.serviceDied(cookie);
+    }
+
+    private long mNativeContext;
+
+    @Override
+    public final native boolean equals(Object other);
+    @Override
+    public final native int hashCode();
+}
diff --git a/android-34/android/os/IBinder.java b/android-34/android/os/IBinder.java
new file mode 100644
index 0000000..90e4b17
--- /dev/null
+++ b/android-34/android/os/IBinder.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import java.io.FileDescriptor;
+
+/**
+ * Base interface for a remotable object, the core part of a lightweight
+ * remote procedure call mechanism designed for high performance when
+ * performing in-process and cross-process calls.  This
+ * interface describes the abstract protocol for interacting with a
+ * remotable object.  Do not implement this interface directly, instead
+ * extend from {@link Binder}.
+ * 
+ * <p>The key IBinder API is {@link #transact transact()} matched by
+ * {@link Binder#onTransact Binder.onTransact()}.  These
+ * methods allow you to send a call to an IBinder object and receive a
+ * call coming in to a Binder object, respectively.  This transaction API
+ * is synchronous, such that a call to {@link #transact transact()} does not
+ * return until the target has returned from
+ * {@link Binder#onTransact Binder.onTransact()}; this is the
+ * expected behavior when calling an object that exists in the local
+ * process, and the underlying inter-process communication (IPC) mechanism
+ * ensures that these same semantics apply when going across processes.
+ * 
+ * <p>The data sent through transact() is a {@link Parcel}, a generic buffer
+ * of data that also maintains some meta-data about its contents.  The meta
+ * data is used to manage IBinder object references in the buffer, so that those
+ * references can be maintained as the buffer moves across processes.  This
+ * mechanism ensures that when an IBinder is written into a Parcel and sent to
+ * another process, if that other process sends a reference to that same IBinder
+ * back to the original process, then the original process will receive the
+ * same IBinder object back.  These semantics allow IBinder/Binder objects to
+ * be used as a unique identity (to serve as a token or for other purposes)
+ * that can be managed across processes.
+ * 
+ * <p>The system maintains a pool of transaction threads in each process that
+ * it runs in.  These threads are used to dispatch all
+ * IPCs coming in from other processes.  For example, when an IPC is made from
+ * process A to process B, the calling thread in A blocks in transact() as
+ * it sends the transaction to process B.  The next available pool thread in
+ * B receives the incoming transaction, calls Binder.onTransact() on the target
+ * object, and replies with the result Parcel.  Upon receiving its result, the
+ * thread in process A returns to allow its execution to continue.  In effect,
+ * other processes appear to use as additional threads that you did not create
+ * executing in your own process.
+ * 
+ * <p>The Binder system also supports recursion across processes.  For example
+ * if process A performs a transaction to process B, and process B while
+ * handling that transaction calls transact() on an IBinder that is implemented
+ * in A, then the thread in A that is currently waiting for the original
+ * transaction to finish will take care of calling Binder.onTransact() on the
+ * object being called by B.  This ensures that the recursion semantics when
+ * calling remote binder object are the same as when calling local objects.
+ * 
+ * <p>When working with remote objects, you often want to find out when they
+ * are no longer valid.  There are three ways this can be determined:
+ * <ul>
+ * <li> The {@link #transact transact()} method will throw a
+ * {@link RemoteException} exception if you try to call it on an IBinder
+ * whose process no longer exists.
+ * <li> The {@link #pingBinder()} method can be called, and will return false
+ * if the remote process no longer exists.
+ * <li> The {@link #linkToDeath linkToDeath()} method can be used to register
+ * a {@link DeathRecipient} with the IBinder, which will be called when its
+ * containing process goes away.
+ * </ul>
+ * 
+ * @see Binder
+ */
+public interface IBinder {
+    /**
+     * The first transaction code available for user commands.
+     */
+    int FIRST_CALL_TRANSACTION  = 0x00000001;
+    /**
+     * The last transaction code available for user commands.
+     */
+    int LAST_CALL_TRANSACTION   = 0x00ffffff;
+    
+    /**
+     * IBinder protocol transaction code: pingBinder().
+     */
+    int PING_TRANSACTION        = ('_'<<24)|('P'<<16)|('N'<<8)|'G';
+    
+    /**
+     * IBinder protocol transaction code: dump internal state.
+     */
+    int DUMP_TRANSACTION        = ('_'<<24)|('D'<<16)|('M'<<8)|'P';
+    
+    /**
+     * IBinder protocol transaction code: execute a shell command.
+     * @hide
+     */
+    int SHELL_COMMAND_TRANSACTION = ('_'<<24)|('C'<<16)|('M'<<8)|'D';
+
+    /**
+     * IBinder protocol transaction code: interrogate the recipient side
+     * of the transaction for its canonical interface descriptor.
+     */
+    int INTERFACE_TRANSACTION   = ('_'<<24)|('N'<<16)|('T'<<8)|'F';
+
+    /**
+     * IBinder protocol transaction code: send a tweet to the target
+     * object.  The data in the parcel is intended to be delivered to
+     * a shared messaging service associated with the object; it can be
+     * anything, as long as it is not more than 130 UTF-8 characters to
+     * conservatively fit within common messaging services.  As part of
+     * {@link Build.VERSION_CODES#HONEYCOMB_MR2}, all Binder objects are
+     * expected to support this protocol for fully integrated tweeting
+     * across the platform.  To support older code, the default implementation
+     * logs the tweet to the main log as a simple emulation of broadcasting
+     * it publicly over the Internet.
+     * 
+     * <p>Also, upon completing the dispatch, the object must make a cup
+     * of tea, return it to the caller, and exclaim "jolly good message
+     * old boy!".
+     */
+    int TWEET_TRANSACTION   = ('_'<<24)|('T'<<16)|('W'<<8)|'T';
+
+    /**
+     * IBinder protocol transaction code: tell an app asynchronously that the
+     * caller likes it.  The app is responsible for incrementing and maintaining
+     * its own like counter, and may display this value to the user to indicate the
+     * quality of the app.  This is an optional command that applications do not
+     * need to handle, so the default implementation is to do nothing.
+     * 
+     * <p>There is no response returned and nothing about the
+     * system will be functionally affected by it, but it will improve the
+     * app's self-esteem.
+     */
+    int LIKE_TRANSACTION   = ('_'<<24)|('L'<<16)|('I'<<8)|'K';
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    int SYSPROPS_TRANSACTION = ('_'<<24)|('S'<<16)|('P'<<8)|'R';
+
+    /**
+     * Flag to {@link #transact}: this is a one-way call, meaning that the
+     * caller returns immediately, without waiting for a result from the
+     * callee. Applies only if the caller and callee are in different
+     * processes.
+     *
+     * <p>The system provides special ordering semantics for multiple oneway calls
+     * being made to the same IBinder object: these calls will be dispatched in the
+     * other process one at a time, with the same order as the original calls.  These
+     * are still dispatched by the IPC thread pool, so may execute on different threads,
+     * but the next one will not be dispatched until the previous one completes.  This
+     * ordering is not guaranteed for calls on different IBinder objects or when mixing
+     * oneway and non-oneway calls on the same IBinder object.</p>
+     */
+    int FLAG_ONEWAY             = 0x00000001;
+
+    /**
+     * Flag to {@link #transact}: request binder driver to clear transaction data.
+     *
+     * Be very careful when using this flag in Java, since Java objects read from a Java
+     * Parcel may be non-trivial to clear.
+     * @hide
+     */
+    int FLAG_CLEAR_BUF          = 0x00000020;
+
+    /**
+     * @hide
+     */
+    int FLAG_COLLECT_NOTED_APP_OPS = 0x00000002;
+
+    /**
+     * Limit that should be placed on IPC sizes to keep them safely under the
+     * transaction buffer limit.
+     * @hide
+     */
+    public static final int MAX_IPC_SIZE = 64 * 1024;
+
+    /**
+     * Limit that should be placed on IPC sizes, in bytes, to keep them safely under the transaction
+     * buffer limit.
+     */
+    static int getSuggestedMaxIpcSizeBytes() {
+        return MAX_IPC_SIZE;
+    }
+
+    /**
+     * Get the canonical name of the interface supported by this binder.
+     */
+    public @Nullable String getInterfaceDescriptor() throws RemoteException;
+
+    /**
+     * Check to see if the object still exists.
+     * 
+     * @return Returns false if the
+     * hosting process is gone, otherwise the result (always by default
+     * true) returned by the pingBinder() implementation on the other
+     * side.
+     */
+    public boolean pingBinder();
+
+    /**
+     * Check to see if the process that the binder is in is still alive.
+     *
+     * @return false if the process is not alive.  Note that if it returns
+     * true, the process may have died while the call is returning.
+     */
+    public boolean isBinderAlive();
+    
+    /**
+     * Attempt to retrieve a local implementation of an interface
+     * for this Binder object.  If null is returned, you will need
+     * to instantiate a proxy class to marshall calls through
+     * the transact() method.
+     */
+    public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);
+
+    /**
+     * Print the object's state into the given stream.
+     * 
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param args additional arguments to the dump request.
+     */
+    public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException;
+
+    /**
+     * Like {@link #dump(FileDescriptor, String[])} but always executes
+     * asynchronously.  If the object is local, a new thread is created
+     * to perform the dump.
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param args additional arguments to the dump request.
+     */
+    public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args)
+            throws RemoteException;
+
+    /**
+     * Execute a shell command on this object.  This may be performed asynchrously from the caller;
+     * the implementation must always call resultReceiver when finished.
+     *
+     * @param in The raw file descriptor that an input data stream can be read from.
+     * @param out The raw file descriptor that normal command messages should be written to.
+     * @param err The raw file descriptor that command error messages should be written to.
+     * @param args Command-line arguments.
+     * @param shellCallback Optional callback to the caller's shell to perform operations in it.
+     * @param resultReceiver Called when the command has finished executing, with the result code.
+     * @hide
+     */
+    public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err,
+            @NonNull String[] args, @Nullable ShellCallback shellCallback,
+            @NonNull ResultReceiver resultReceiver) throws RemoteException;
+
+    /**
+     * Get the binder extension of this binder interface.
+     * This allows one to customize an interface without having to modify the original interface.
+     *
+     * @return null if don't have binder extension
+     * @throws RemoteException
+     * @hide
+     */
+    public default @Nullable IBinder getExtension() throws RemoteException {
+        throw new IllegalStateException("Method is not implemented");
+    }
+
+    /**
+     * Perform a generic operation with the object.
+     * 
+     * @param code The action to perform.  This should
+     * be a number between {@link #FIRST_CALL_TRANSACTION} and
+     * {@link #LAST_CALL_TRANSACTION}.
+     * @param data Marshalled data to send to the target.  Must not be null.
+     * If you are not sending any data, you must create an empty Parcel
+     * that is given here.
+     * @param reply Marshalled data to be received from the target.  May be
+     * null if you are not interested in the return value.
+     * @param flags Additional operation flags.  Either 0 for a normal
+     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+     *
+     * @return Returns the result from {@link Binder#onTransact}.  A successful call
+     * generally returns true; false generally means the transaction code was not
+     * understood.  For a oneway call to a different process false should never be
+     * returned.  If a oneway call is made to code in the same process (usually to
+     * a C++ or Rust implementation), then there are no oneway semantics, and
+     * false can still be returned.
+     */
+    public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
+        throws RemoteException;
+
+    /**
+     * Interface for receiving a callback when the process hosting an IBinder
+     * has gone away.
+     * 
+     * @see #linkToDeath
+     */
+    public interface DeathRecipient {
+        public void binderDied();
+
+        /**
+         * Interface for receiving a callback when the process hosting an IBinder
+         * has gone away.
+         * @param who The IBinder that has become invalid
+         */
+        default void binderDied(@NonNull IBinder who) {
+            binderDied();
+        }
+    }
+
+    /**
+     * Register the recipient for a notification if this binder
+     * goes away.  If this binder object unexpectedly goes away
+     * (typically because its hosting process has been killed),
+     * then the given {@link DeathRecipient}'s
+     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
+     * will be called.
+     *
+     * <p>This will automatically be unlinked when all references to the linked
+     * binder proxy are dropped.</p>
+     *
+     * <p>You will only receive death notifications for remote binders,
+     * as local binders by definition can't die without you dying as well.</p>
+     *
+     * @throws RemoteException if the target IBinder's
+     * process has already died.
+     *
+     * @see #unlinkToDeath
+     */
+    public void linkToDeath(@NonNull DeathRecipient recipient, int flags)
+            throws RemoteException;
+
+    /**
+     * Remove a previously registered death notification.
+     * The recipient will no longer be called if this object
+     * dies.
+     * 
+     * @return {@code true} if the <var>recipient</var> is successfully
+     * unlinked, assuring you that its
+     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
+     * will not be called;  {@code false} if the target IBinder has already
+     * died, meaning the method has been (or soon will be) called.
+     * 
+     * @throws java.util.NoSuchElementException if the given
+     * <var>recipient</var> has not been registered with the IBinder, and
+     * the IBinder is still alive.  Note that if the <var>recipient</var>
+     * was never registered, but the IBinder has already died, then this
+     * exception will <em>not</em> be thrown, and you will receive a false
+     * return value instead.
+     */
+    public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
+}
diff --git a/android-34/android/os/IHwBinder.java b/android-34/android/os/IHwBinder.java
new file mode 100644
index 0000000..249eb3a
--- /dev/null
+++ b/android-34/android/os/IHwBinder.java
@@ -0,0 +1,70 @@
+/*
+ * 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.os;
+
+import android.annotation.SystemApi;
+
+/** @hide */
+@SystemApi
+public interface IHwBinder {
+    /**
+     * Process a hwbinder transaction.
+     *
+     * @param code interface specific code for interface.
+     * @param request parceled transaction
+     * @param reply object to parcel reply into
+     * @param flags transaction flags to be chosen by wire protocol
+     */
+    public void transact(
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
+
+    /**
+     * Return as IHwInterface instance only if this implements descriptor.
+     *
+     * @param descriptor for example [email protected]::IBaz
+     */
+    public IHwInterface queryLocalInterface(String descriptor);
+
+    /**
+     * Interface for receiving a callback when the process hosting a service
+     * has gone away.
+     */
+    public interface DeathRecipient {
+        /**
+         * Callback for a registered process dying.
+         *
+         * @param cookie cookie this death recipient was registered with.
+         */
+        public void serviceDied(long cookie);
+    }
+
+    /**
+     * Notifies the death recipient with the cookie when the process containing
+     * this binder dies.
+     *
+     * @param recipient callback object to be called on object death.
+     * @param cookie value to be given to callback on object death.
+     */
+    public boolean linkToDeath(DeathRecipient recipient, long cookie);
+    /**
+     * Unregisters the death recipient from this binder.
+     *
+     * @param recipient callback to no longer recieve death notifications on this binder.
+     */
+    public boolean unlinkToDeath(DeathRecipient recipient);
+}
diff --git a/android-34/android/os/IHwInterface.java b/android-34/android/os/IHwInterface.java
new file mode 100644
index 0000000..f21f6e3
--- /dev/null
+++ b/android-34/android/os/IHwInterface.java
@@ -0,0 +1,28 @@
+/*
+ * 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.os;
+
+import android.annotation.SystemApi;
+
+/** @hide */
+@SystemApi
+public interface IHwInterface {
+    /**
+     * @return the binder object that corresponds to this interface.
+     */
+    public IHwBinder asBinder();
+}
diff --git a/android-34/android/os/IInterface.java b/android-34/android/os/IInterface.java
new file mode 100644
index 0000000..2a2605a
--- /dev/null
+++ b/android-34/android/os/IInterface.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+/**
+ * Base class for Binder interfaces.  When defining a new interface,
+ * you must derive it from IInterface.
+ */
+public interface IInterface
+{
+    /**
+     * Retrieve the Binder object associated with this interface.
+     * You must use this instead of a plain cast, so that proxy objects
+     * can return the correct result.
+     */
+    public IBinder asBinder();
+}
diff --git a/android-34/android/os/IncidentManager.java b/android-34/android/os/IncidentManager.java
new file mode 100644
index 0000000..b97993a
--- /dev/null
+++ b/android-34/android/os/IncidentManager.java
@@ -0,0 +1,804 @@
+/*
+ * 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.os;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Slog;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Class to take an incident report.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.INCIDENT_SERVICE)
+public class IncidentManager {
+    private static final String TAG = "IncidentManager";
+
+    /**
+     * Authority for pending report id urls.
+     *
+     * @hide
+     */
+    public static final String URI_SCHEME = "content";
+
+    /**
+     * Authority for pending report id urls.
+     *
+     * @hide
+     */
+    public static final String URI_AUTHORITY = "android.os.IncidentManager";
+
+    /**
+     * Authority for pending report id urls.
+     *
+     * @hide
+     */
+    public static final String URI_PATH = "/pending";
+
+    /**
+     * Query parameter for the uris for the pending report id.
+     *
+     * @hide
+     */
+    public static final String URI_PARAM_ID = "id";
+
+    /**
+     * Query parameter for the uris for the incident report id.
+     *
+     * @hide
+     */
+    public static final String URI_PARAM_REPORT_ID = "r";
+
+    /**
+     * Query parameter for the uris for the pending report id.
+     *
+     * @hide
+     */
+    public static final String URI_PARAM_CALLING_PACKAGE = "pkg";
+
+    /**
+     * Query parameter for the uris for the pending report id, in wall clock
+     * ({@link System.currentTimeMillis()}) timebase.
+     *
+     * @hide
+     */
+    public static final String URI_PARAM_TIMESTAMP = "t";
+
+    /**
+     * Query parameter for the uris for the pending report id.
+     *
+     * @hide
+     */
+    public static final String URI_PARAM_FLAGS = "flags";
+
+    /**
+     * Query parameter for the uris for the pending report id.
+     *
+     * @hide
+     */
+    public static final String URI_PARAM_RECEIVER_CLASS = "receiver";
+
+    /**
+     * Do the confirmation with a dialog instead of the default, which is a notification.
+     * It is possible for the dialog to be downgraded to a notification in some cases.
+     */
+    public static final int FLAG_CONFIRMATION_DIALOG = 0x1;
+
+    /**
+     * Flag marking whether corresponding pending report allows consentless bugreport.
+     */
+    public static final int FLAG_ALLOW_CONSENTLESS_BUGREPORT = 0x2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_CONFIRMATION_DIALOG,
+            FLAG_ALLOW_CONSENTLESS_BUGREPORT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PendingReportFlags {}
+
+    /**
+     * Flag marking fields and incident reports than can be taken
+     * off the device only via adb.
+     */
+    public static final int PRIVACY_POLICY_LOCAL = 0;
+
+    /**
+     * Flag marking fields and incident reports than can be taken
+     * off the device with contemporary consent.
+     */
+    public static final int PRIVACY_POLICY_EXPLICIT = 100;
+
+    /**
+     * Flag marking fields and incident reports than can be taken
+     * off the device with prior consent.
+     */
+    public static final int PRIVACY_POLICY_AUTO = 200;
+
+    /** @hide */
+    @IntDef(flag = false, prefix = { "PRIVACY_POLICY_" }, value = {
+            PRIVACY_POLICY_AUTO,
+            PRIVACY_POLICY_EXPLICIT,
+            PRIVACY_POLICY_LOCAL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PrivacyPolicy {}
+
+    private final Context mContext;
+
+    private Object mLock = new Object();
+    private IIncidentManager mIncidentService;
+    private IIncidentCompanion mCompanionService;
+
+    /**
+     * Record for a report that has been taken and is pending user authorization
+     * to share it.
+     * @hide
+     */
+    @SystemApi
+    public static class PendingReport {
+        /**
+         * Encoded data.
+         */
+        private final Uri mUri;
+
+        /**
+         * URI_PARAM_FLAGS from the uri
+         */
+        private final int mFlags;
+
+        /**
+         * URI_PARAM_CALLING_PACKAGE from the uri
+         */
+        private final String mRequestingPackage;
+
+        /**
+         * URI_PARAM_TIMESTAMP from the uri
+         */
+        private final long mTimestamp;
+
+        /**
+         * Constructor.
+         */
+        public PendingReport(@NonNull Uri uri) {
+            int flags = 0;
+            try {
+                flags = Integer.parseInt(uri.getQueryParameter(URI_PARAM_FLAGS));
+            } catch (NumberFormatException ex) {
+                throw new RuntimeException("Invalid URI: No " + URI_PARAM_FLAGS
+                        + " parameter. " + uri);
+            }
+            mFlags = flags;
+
+            String requestingPackage = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE);
+            if (requestingPackage == null) {
+                throw new RuntimeException("Invalid URI: No " + URI_PARAM_CALLING_PACKAGE
+                        + " parameter. " + uri);
+            }
+            mRequestingPackage = requestingPackage;
+
+            long timestamp = -1;
+            try {
+                timestamp = Long.parseLong(uri.getQueryParameter(URI_PARAM_TIMESTAMP));
+            } catch (NumberFormatException ex) {
+                throw new RuntimeException("Invalid URI: No " + URI_PARAM_TIMESTAMP
+                        + " parameter. " + uri);
+            }
+            mTimestamp = timestamp;
+
+            mUri = uri;
+        }
+
+        /**
+         * Get the package with which this report will be shared.
+         */
+        public @NonNull String getRequestingPackage() {
+            return mRequestingPackage;
+        }
+
+        /**
+         * Get the flags requested for this pending report.
+         *
+         * @see PendingReportFlags
+         */
+        @PendingReportFlags
+        public int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * Get the time this pending report was posted.
+         */
+        public long getTimestamp() {
+            return mTimestamp;
+        }
+
+        /**
+         * Get the URI associated with this PendingReport.  It can be used to
+         * re-retrieve it from {@link IncidentManager} or set as the data field of
+         * an Intent.
+         */
+        public @NonNull Uri getUri() {
+            return mUri;
+        }
+
+        /**
+         * String representation of this PendingReport.
+         */
+        @Override
+        public @NonNull String toString() {
+            return "PendingReport(" + getUri().toString() + ")";
+        }
+
+        /**
+         * @inheritDoc
+         */
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof PendingReport)) {
+                return false;
+            }
+            final PendingReport that = (PendingReport) obj;
+            return this.mUri.equals(that.mUri)
+                    && this.mFlags == that.mFlags
+                    && this.mRequestingPackage.equals(that.mRequestingPackage)
+                    && this.mTimestamp == that.mTimestamp;
+        }
+    }
+
+    /**
+     * Record of an incident report that has previously been taken.
+     * @hide
+     */
+    @SystemApi
+    public static class IncidentReport implements Parcelable, Closeable {
+        private final long mTimestampNs;
+        private final int mPrivacyPolicy;
+        private ParcelFileDescriptor mFileDescriptor;
+
+        public IncidentReport(Parcel in) {
+            mTimestampNs = in.readLong();
+            mPrivacyPolicy = in.readInt();
+            if (in.readInt() != 0) {
+                mFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+            } else {
+                mFileDescriptor = null;
+            }
+        }
+
+        /**
+         * Close the input stream associated with this entry.
+         */
+        public void close() {
+            try {
+                if (mFileDescriptor != null) {
+                    mFileDescriptor.close();
+                    mFileDescriptor = null;
+                }
+            } catch (IOException e) {
+            }
+        }
+
+        /**
+         * Get the time at which this incident report was taken, in wall clock time
+         * ({@link System#currenttimeMillis System.currenttimeMillis()} time base).
+         */
+        public long getTimestamp() {
+            return mTimestampNs / 1000000;
+        }
+
+        /**
+         * Get the privacy level to which this report has been filtered.
+         *
+         * @see #PRIVACY_POLICY_AUTO
+         * @see #PRIVACY_POLICY_EXPLICIT
+         * @see #PRIVACY_POLICY_LOCAL
+         */
+        public long getPrivacyPolicy() {
+            return mPrivacyPolicy;
+        }
+
+        /**
+         * Get the contents of this incident report.
+         */
+        public InputStream getInputStream() throws IOException {
+            if (mFileDescriptor == null) {
+                return null;
+            }
+            return new ParcelFileDescriptor.AutoCloseInputStream(mFileDescriptor);
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public int describeContents() {
+            return mFileDescriptor != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeLong(mTimestampNs);
+            out.writeInt(mPrivacyPolicy);
+            if (mFileDescriptor != null) {
+                out.writeInt(1);
+                mFileDescriptor.writeToParcel(out, flags);
+            } else {
+                out.writeInt(0);
+            }
+        }
+
+        /**
+         * {@link Parcelable.Creator Creator} for {@link IncidentReport}.
+         */
+        public static final @android.annotation.NonNull Parcelable.Creator<IncidentReport> CREATOR = new Parcelable.Creator() {
+            /**
+             * @inheritDoc
+             */
+            public IncidentReport[] newArray(int size) {
+                return new IncidentReport[size];
+            }
+
+            /**
+             * @inheritDoc
+             */
+            public IncidentReport createFromParcel(Parcel in) {
+                return new IncidentReport(in);
+            }
+        };
+    }
+
+    /**
+     * Listener for the status of an incident report being authorized or denied.
+     *
+     * @see #requestAuthorization
+     * @see #cancelAuthorization
+     */
+    public static class AuthListener {
+        Executor mExecutor;
+
+        IIncidentAuthListener.Stub mBinder = new IIncidentAuthListener.Stub() {
+            @Override
+            public void onReportApproved() {
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        AuthListener.this.onReportApproved();
+                    });
+                } else {
+                    AuthListener.this.onReportApproved();
+                }
+            }
+
+            @Override
+            public void onReportDenied() {
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        AuthListener.this.onReportDenied();
+                    });
+                } else {
+                    AuthListener.this.onReportDenied();
+                }
+            }
+        };
+
+        /**
+         * Called when a report is approved.
+         */
+        public void onReportApproved() {
+        }
+
+        /**
+         * Called when a report is denied.
+         */
+        public void onReportDenied() {
+        }
+    }
+
+    /**
+     * Callback for dumping an extended (usually vendor-supplied) incident report section
+     *
+     * @see #registerSection
+     * @see #unregisterSection
+     */
+    public static class DumpCallback {
+        private int mId;
+        private Executor mExecutor;
+
+        IIncidentDumpCallback.Stub mBinder = new IIncidentDumpCallback.Stub() {
+            @Override
+            public void onDumpSection(ParcelFileDescriptor pfd) {
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        DumpCallback.this.onDumpSection(mId,
+                                new ParcelFileDescriptor.AutoCloseOutputStream(pfd));
+                    });
+                } else {
+                    DumpCallback.this.onDumpSection(mId,
+                            new ParcelFileDescriptor.AutoCloseOutputStream(pfd));
+                }
+            }
+        };
+
+        /**
+         * Dump the registered section as a protobuf message to the given OutputStream. Called when
+         * incidentd requests to dump this section.
+         *
+         * @param id  the id of the registered section. The same id used in calling
+         *            {@link #registerSection(int, String, DumpCallback)} will be passed in here.
+         * @param out the OutputStream to write the protobuf message
+         */
+        public void onDumpSection(int id, @NonNull OutputStream out) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public IncidentManager(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Take an incident report.
+     */
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.DUMP,
+            android.Manifest.permission.PACKAGE_USAGE_STATS
+    })
+    public void reportIncident(IncidentReportArgs args) {
+        reportIncidentInternal(args);
+    }
+
+    /**
+     * Request authorization of an incident report.
+     */
+    @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL)
+    public void requestAuthorization(int callingUid, String callingPackage, int flags,
+            AuthListener listener) {
+        requestAuthorization(callingUid, callingPackage, flags,
+                mContext.getMainExecutor(), listener);
+    }
+
+    /**
+     * Request authorization of an incident report.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL)
+    public void requestAuthorization(int callingUid, @NonNull String callingPackage, int flags,
+             @NonNull @CallbackExecutor Executor executor, @NonNull AuthListener listener) {
+        try {
+            if (listener.mExecutor != null) {
+                throw new RuntimeException("Do not reuse AuthListener objects when calling"
+                        + " requestAuthorization");
+            }
+            listener.mExecutor = executor;
+            getCompanionServiceLocked().authorizeReport(callingUid, callingPackage, null, null,
+                    flags, listener.mBinder);
+        } catch (RemoteException ex) {
+            // System process going down
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Cancel a previous request for incident report authorization.
+     */
+    @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL)
+    public void cancelAuthorization(AuthListener listener) {
+        try {
+            getCompanionServiceLocked().cancelAuthorization(listener.mBinder);
+        } catch (RemoteException ex) {
+            // System process going down
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Get incident (and bug) reports that are pending approval to share.
+     */
+    @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS)
+    public List<PendingReport> getPendingReports() {
+        List<String> strings;
+        try {
+            strings = getCompanionServiceLocked().getPendingReports();
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        }
+        final int size = strings.size();
+        ArrayList<PendingReport> result = new ArrayList(size);
+        for (int i = 0; i < size; i++) {
+            result.add(new PendingReport(Uri.parse(strings.get(i))));
+        }
+        return result;
+    }
+
+    /**
+     * Allow this report to be shared with the given app.
+     */
+    @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS)
+    public void approveReport(Uri uri) {
+        try {
+            getCompanionServiceLocked().approveReport(uri.toString());
+        } catch (RemoteException ex) {
+            // System process going down
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Do not allow this report to be shared with the given app.
+     */
+    @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS)
+    public void denyReport(Uri uri) {
+        try {
+            getCompanionServiceLocked().denyReport(uri.toString());
+        } catch (RemoteException ex) {
+            // System process going down
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Register a callback to dump an extended incident report section with the given id and name,
+     * running on the supplied executor.
+     *
+     * Calling <code>registerSection</code> with a duplicate id will override previous registration.
+     * However, the request must come from the same calling uid.
+     *
+     * @param id       the ID of the extended section. It should be unique system-wide, and be
+     *                 different from IDs of all existing section in
+     *                 frameworks/base/core/proto/android/os/incident.proto.
+     *                 Also see incident.proto for other rules about the ID.
+     * @param name     the name to display in logs and/or stderr when taking an incident report
+     *                 containing this section, mainly for debugging purpose
+     * @param executor the executor used to run the callback
+     * @param callback the callback function to be invoked when an incident report with all sections
+     *                 or sections matching the given id is being taken
+     */
+    public void registerSection(int id, @NonNull String name,
+                @NonNull @CallbackExecutor Executor executor, @NonNull DumpCallback callback) {
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(callback, "callback cannot be null");
+        try {
+            if (callback.mExecutor != null) {
+                throw new RuntimeException("Do not reuse DumpCallback objects when calling"
+                        + " registerSection");
+            }
+            callback.mExecutor = executor;
+            callback.mId = id;
+            final IIncidentManager service = getIIncidentManagerLocked();
+            if (service == null) {
+                Slog.e(TAG, "registerSection can't find incident binder service");
+                return;
+            }
+            service.registerSection(id, name, callback.mBinder);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "registerSection failed", ex);
+        }
+    }
+
+    /**
+     * Unregister an extended section dump function. The section must be previously registered with
+     * {@link #registerSection(int, String, DumpCallback)} by the same calling uid.
+     */
+    public void unregisterSection(int id) {
+        try {
+            final IIncidentManager service = getIIncidentManagerLocked();
+            if (service == null) {
+                Slog.e(TAG, "unregisterSection can't find incident binder service");
+                return;
+            }
+            service.unregisterSection(id);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "unregisterSection failed", ex);
+        }
+    }
+
+    /**
+     * Get the incident reports that are available for upload for the supplied
+     * broadcast recevier.
+     *
+     * @param receiverClass Class name of broadcast receiver in this package that
+     *   was registered to retrieve reports.
+     *
+     * @return A list of {@link Uri Uris} that are awaiting upload.
+     */
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.DUMP,
+            android.Manifest.permission.PACKAGE_USAGE_STATS
+    })
+    public @NonNull List<Uri> getIncidentReportList(String receiverClass) {
+        List<String> strings;
+        try {
+            strings = getCompanionServiceLocked().getIncidentReportList(
+                    mContext.getPackageName(), receiverClass);
+        } catch (RemoteException ex) {
+            throw new RuntimeException("System server or incidentd going down", ex);
+        }
+        final int size = strings.size();
+        ArrayList<Uri> result = new ArrayList(size);
+        for (int i = 0; i < size; i++) {
+            result.add(Uri.parse(strings.get(i)));
+        }
+        return result;
+    }
+
+    /**
+     * Get the incident report with the given URI id.
+     *
+     * @param uri Identifier of the incident report.
+     *
+     * @return an IncidentReport object, or null if the incident report has been
+     *  expired from disk.
+     */
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.DUMP,
+            android.Manifest.permission.PACKAGE_USAGE_STATS
+    })
+    public @Nullable IncidentReport getIncidentReport(Uri uri) {
+        final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID);
+        if (id == null) {
+            // If there's no report id, it's a bug report, so we can't return the incident
+            // report.
+            return null;
+        }
+
+        final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE);
+        if (pkg == null) {
+            throw new RuntimeException("Invalid URI: No "
+                    + URI_PARAM_CALLING_PACKAGE + " parameter. " + uri);
+        }
+
+        final String cls = uri.getQueryParameter(URI_PARAM_RECEIVER_CLASS);
+        if (cls == null) {
+            throw new RuntimeException("Invalid URI: No "
+                    + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri);
+        }
+
+        try {
+            return getCompanionServiceLocked().getIncidentReport(pkg, cls, id);
+        } catch (RemoteException ex) {
+            throw new RuntimeException("System server or incidentd going down", ex);
+        }
+    }
+
+    /**
+     * Delete the incident report with the given URI id.
+     *
+     * @param uri Identifier of the incident report. Pass null to delete all
+     *              incident reports owned by this application.
+     */
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.DUMP,
+            android.Manifest.permission.PACKAGE_USAGE_STATS
+    })
+    public void deleteIncidentReports(Uri uri) {
+        if (uri == null) {
+            try {
+                getCompanionServiceLocked().deleteAllIncidentReports(mContext.getPackageName());
+            } catch (RemoteException ex) {
+                throw new RuntimeException("System server or incidentd going down", ex);
+            }
+        } else {
+            final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE);
+            if (pkg == null) {
+                throw new RuntimeException("Invalid URI: No "
+                        + URI_PARAM_CALLING_PACKAGE + " parameter. " + uri);
+            }
+
+            final String cls = uri.getQueryParameter(URI_PARAM_RECEIVER_CLASS);
+            if (cls == null) {
+                throw new RuntimeException("Invalid URI: No "
+                        + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri);
+            }
+
+            final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID);
+            if (id == null) {
+                throw new RuntimeException("Invalid URI: No "
+                        + URI_PARAM_REPORT_ID + " parameter. " + uri);
+            }
+
+            try {
+                getCompanionServiceLocked().deleteIncidentReports(pkg, cls, id);
+            } catch (RemoteException ex) {
+                throw new RuntimeException("System server or incidentd going down", ex);
+            }
+        }
+    }
+
+    private void reportIncidentInternal(IncidentReportArgs args) {
+        try {
+            final IIncidentManager service = getIIncidentManagerLocked();
+            if (service == null) {
+                Slog.e(TAG, "reportIncident can't find incident binder service");
+                return;
+            }
+            service.reportIncident(args);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "reportIncident failed", ex);
+        }
+    }
+
+    private IIncidentManager getIIncidentManagerLocked() throws RemoteException {
+        if (mIncidentService != null) {
+            return mIncidentService;
+        }
+
+        synchronized (mLock) {
+            if (mIncidentService != null) {
+                return mIncidentService;
+            }
+            mIncidentService = IIncidentManager.Stub.asInterface(
+                ServiceManager.getService(Context.INCIDENT_SERVICE));
+            if (mIncidentService != null) {
+                mIncidentService.asBinder().linkToDeath(() -> {
+                    synchronized (mLock) {
+                        mIncidentService = null;
+                    }
+                }, 0);
+            }
+            return mIncidentService;
+        }
+    }
+
+    private IIncidentCompanion getCompanionServiceLocked() throws RemoteException {
+        if (mCompanionService != null) {
+            return mCompanionService;
+        }
+
+        synchronized (this) {
+            if (mCompanionService != null) {
+                return mCompanionService;
+            }
+            mCompanionService = IIncidentCompanion.Stub.asInterface(
+                ServiceManager.getService(Context.INCIDENT_COMPANION_SERVICE));
+            if (mCompanionService != null) {
+                mCompanionService.asBinder().linkToDeath(() -> {
+                    synchronized (mLock) {
+                        mCompanionService = null;
+                    }
+                }, 0);
+            }
+            return mCompanionService;
+        }
+    }
+}
+
diff --git a/android-34/android/os/IncidentReportArgs.java b/android-34/android/os/IncidentReportArgs.java
new file mode 100644
index 0000000..73e4914
--- /dev/null
+++ b/android-34/android/os/IncidentReportArgs.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2005 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.IntArray;
+
+import java.util.ArrayList;
+
+/**
+ * The arguments for an incident report.
+ * {@hide}
+ */
+@SystemApi
+public final class IncidentReportArgs implements Parcelable {
+
+    private final IntArray mSections = new IntArray();
+    private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
+    private boolean mAll;
+    private int mPrivacyPolicy;
+    private String mReceiverPkg;
+    private String mReceiverCls;
+
+    /**
+     * Construct an incident report args with no fields.
+     */
+    public IncidentReportArgs() {
+        mPrivacyPolicy = IncidentManager.PRIVACY_POLICY_AUTO;
+    }
+
+    /**
+     * Construct an incdent report args from the given parcel.
+     */
+    public IncidentReportArgs(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAll ? 1 : 0);
+
+        int N = mSections.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            out.writeInt(mSections.get(i));
+        }
+
+        N = mHeaders.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            out.writeByteArray(mHeaders.get(i));
+        }
+
+        out.writeInt(mPrivacyPolicy);
+
+        out.writeString(mReceiverPkg);
+
+        out.writeString(mReceiverCls);
+    }
+
+    public void readFromParcel(Parcel in) {
+        mAll = in.readInt() != 0;
+
+        mSections.clear();
+        int N = in.readInt();
+        for (int i=0; i<N; i++) {
+            mSections.add(in.readInt());
+        }
+
+        mHeaders.clear();
+        N = in.readInt();
+        for (int i=0; i<N; i++) {
+            mHeaders.add(in.createByteArray());
+        }
+
+        mPrivacyPolicy = in.readInt();
+
+        mReceiverPkg = in.readString();
+
+        mReceiverCls = in.readString();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<IncidentReportArgs> CREATOR
+            = new Parcelable.Creator<IncidentReportArgs>() {
+        public IncidentReportArgs createFromParcel(Parcel in) {
+            return new IncidentReportArgs(in);
+        }
+
+        public IncidentReportArgs[] newArray(int size) {
+            return new IncidentReportArgs[size];
+        }
+    };
+
+    /**
+     * Print this report as a string.
+     */
+    @NonNull
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("Incident(");
+        if (mAll) {
+            sb.append("all");
+        } else {
+            final int N = mSections.size();
+            if (N > 0) {
+                sb.append(mSections.get(0));
+            }
+            for (int i=1; i<N; i++) {
+                sb.append(" ");
+                sb.append(mSections.get(i));
+            }
+        }
+        sb.append(", ");
+        sb.append(mHeaders.size());
+        sb.append(" headers), ");
+        sb.append("privacy: ").append(mPrivacyPolicy);
+        sb.append("receiver pkg: ").append(mReceiverPkg);
+        sb.append("receiver cls: ").append(mReceiverCls);
+        return sb.toString();
+    }
+
+    /**
+     * Set this incident report to include all fields.
+     */
+    public void setAll(boolean all) {
+        mAll = all;
+        if (all) {
+            mSections.clear();
+        }
+    }
+
+    /**
+     * Set this incident report privacy policy spec.
+     */
+    public void setPrivacyPolicy(int privacyPolicy) {
+        switch (privacyPolicy) {
+            case IncidentManager.PRIVACY_POLICY_LOCAL:
+            case IncidentManager.PRIVACY_POLICY_EXPLICIT:
+            case IncidentManager.PRIVACY_POLICY_AUTO:
+                mPrivacyPolicy = privacyPolicy;
+                break;
+            default:
+                mPrivacyPolicy = IncidentManager.PRIVACY_POLICY_AUTO;
+        }
+    }
+
+    /**
+     * Add this section to the incident report. Skip if the input is smaller than 2 since section
+     * id are only valid for positive integer as Protobuf field id. Here 1 is reserved for Header.
+     */
+    public void addSection(int section) {
+        if (!mAll && section > 1) {
+            mSections.add(section);
+        }
+    }
+
+    /**
+     * Returns whether the incident report will include all fields.
+     */
+    public boolean isAll() {
+        return mAll;
+    }
+
+    /**
+     * Returns whether this section will be included in the incident report.
+     */
+    public boolean containsSection(int section) {
+        return mAll || mSections.indexOf(section) >= 0;
+    }
+
+    public int sectionCount() {
+        return mSections.size();
+    }
+
+    public void addHeader(byte[] header) {
+        mHeaders.add(header);
+    }
+}
+
diff --git a/android-34/android/os/InputConstants.java b/android-34/android/os/InputConstants.java
new file mode 100644
index 0000000..c8fa065
--- /dev/null
+++ b/android-34/android/os/InputConstants.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+/** @hide */
+public class InputConstants {
+    public static final int DEFAULT_DISPATCHING_TIMEOUT_MILLIS =
+            IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
+            * Build.HW_TIMEOUT_MULTIPLIER;
+}
diff --git a/android-34/android/os/IpcDataCache.java b/android-34/android/os/IpcDataCache.java
new file mode 100644
index 0000000..bf44d65
--- /dev/null
+++ b/android-34/android/os/IpcDataCache.java
@@ -0,0 +1,590 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.PropertyInvalidatedCache;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FastPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
+ * but doesn't hold a lock across data fetches on query misses.
+ *
+ * The intended use case is caching frequently-read, seldom-changed information normally retrieved
+ * across interprocess communication. Imagine that you've written a user birthday information
+ * daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface over
+ * binder. That binder interface looks something like this:
+ *
+ * <pre>
+ * parcelable Birthday {
+ *   int month;
+ *   int day;
+ * }
+ * interface IUserBirthdayService {
+ *   Birthday getUserBirthday(int userId);
+ * }
+ * </pre>
+ *
+ * Suppose the service implementation itself looks like this...
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl implements IUserBirthdayService {
+ *   private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
+ *   {@literal @}Override
+ *   public synchronized Birthday getUserBirthday(int userId) {
+ *     return mUidToBirthday.get(userId);
+ *   }
+ *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
+ *     mUidToBirthday.clear();
+ *     mUidToBirthday.putAll(uidToBirthday);
+ *   }
+ * }
+ * </pre>
+ *
+ * ... and we have a client in frameworks (loaded into every app process) that looks like this:
+ *
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   public Birthday getUserBirthday(int userId) {
+ *     return GetService("birthdayd").getUserBirthday(userId);
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call to
+ * the birthdayd process and consult its database of birthdays. If we query user birthdays
+ * frequently, we do a lot of work that we don't have to do, since user birthdays change
+ * infrequently.
+ *
+ * IpcDataCache is part of a pattern for optimizing this kind of information-querying code. Using
+ * {@code IpcDataCache}, you'd write the client this way:
+ *
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ *           {@literal @}Override
+ *           public Birthday apply(Integer) {
+ *              return GetService("birthdayd").getUserBirthday(userId);
+ *           }
+ *       };
+ *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
+ *   private static final String BDAY_API = "getUserBirthday";
+ *   private final IpcDataCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ *     IpcDataCache&lt;Integer, Birthday%&gt;(
+ *             BDAY_CACHE_MAX, MODULE_SYSTEM, BDAY_API,  BDAY_API, mBirthdayQuery);
+ *
+ *   public void disableUserBirthdayCache() {
+ *     mBirthdayCache.disableForCurrentProcess();
+ *   }
+ *   public void invalidateUserBirthdayCache() {
+ *     mBirthdayCache.invalidateCache();
+ *   }
+ *   public Birthday getUserBirthday(int userId) {
+ *     return mBirthdayCache.query(userId);
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
+ * for the first time; on subsequent queries, we return the already-known Birthday object.
+ *
+ * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
+ * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
+ * string is permitted.  The third parameters is the name of the API being cached; this, too, can
+ * any value.  The fourth is the name of the cache.  The cache is usually named after th API.
+ * Some things you must know about the three strings:
+ * <list>
+ * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
+ * Usually, the SELinux rules permit a process to write a system property (and therefore
+ * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}.  This means that
+ * although the cache can be constructed with any module string, whatever string is chosen must be
+ * consistent with the SELinux configuration.
+ * <ul> The API name can be any string of alphanumeric characters.  All caches with the same API
+ * are invalidated at the same time.  If a server supports several caches and all are invalidated
+ * in common, then it is most efficient to assign the same API string to every cache.
+ * <ul> The cache name can be any string.  In debug output, the name is used to distiguish between
+ * caches with the same API name.  The cache name is also used when disabling caches in the
+ * current process.  So, invalidation is based on the module+api but disabling (which is generally
+ * a once-per-process operation) is based on the cache name.
+ * </list>
+ *
+ * User birthdays do occasionally change, so we have to modify the server to invalidate this
+ * cache when necessary. That invalidation code looks like this:
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl {
+ *   ...
+ *   public UserBirthdayServiceImpl() {
+ *     ...
+ *     ActivityThread.currentActivityThread().disableUserBirthdayCache();
+ *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ *   }
+ *
+ *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
+ *     mUidToBirthday.clear();
+ *     mUidToBirthday.putAll(uidToBirthday);
+ *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * The call to {@code IpcDataCache.invalidateCache()} guarantees that all clients will re-fetch
+ * birthdays from binder during consequent calls to
+ * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
+ * held, we maintain consistency between different client views of the birthday state. The use of
+ * IpcDataCache in this idiomatic way introduces no new race conditions.
+ *
+ * IpcDataCache has a few other features for doing things like incremental enhancement of cached
+ * values and invalidation of multiple caches (that all share the same property key) at once.
+ *
+ * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
+ * time we update the cache. SELinux configuration must allow everyone to read this property
+ * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
+ * the property. (These properties conventionally begin with the "cache_key." prefix.)
+ *
+ * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
+ * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In this
+ * local case, there's no IPC, so use of the cache is (depending on exact circumstance)
+ * unnecessary.
+ *
+ * There may be queries for which it is more efficient to bypass the cache than to cache the
+ * result.  This would be true, for example, if some queries would require frequent cache
+ * invalidation while other queries require infrequent invalidation.  To expand on the birthday
+ * example, suppose that there is a userId that signifies "the next birthday".  When passed this
+ * userId, the server returns the next birthday among all users - this value changes as time
+ * advances.  The userId value can be cached, but the cache must be invalidated whenever a
+ * birthday occurs, and this invalidates all birthdays.  If there is a large number of users,
+ * invalidation will happen so often that the cache provides no value.
+ *
+ * The class provides a bypass mechanism to handle this situation.
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
+ *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
+ *           {@literal @}Override
+ *           public Birthday apply(Integer) {
+ *              return GetService("birthdayd").getUserBirthday(userId);
+ *           }
+ *           {@literal @}Override
+ *           public boolean shouldBypassQuery(Integer userId) {
+ *               return userId == NEXT_BIRTHDAY;
+ *           }
+ *       };
+ *   ...
+ * }
+ * </pre>
+ *
+ * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
+ * particular query.  The {@code shouldBypassQuery()} method is not abstract and the default
+ * implementation returns false.
+ *
+ * For security, there is a allowlist of processes that are allowed to invalidate a cache.  The
+ * allowlist includes normal runtime processes but does not include test processes.  Test
+ * processes must call {@code IpcDataCache.disableForTestMode()} to disable all cache activity in
+ * that process.
+ *
+ * Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
+ *
+ * To test a binder cache, create one or more tests that exercise the binder method.  This should
+ * be done twice: once with production code and once with a special image that sets {@code DEBUG}
+ * and {@code VERIFY} true.  In the latter case, verify that no cache inconsistencies are
+ * reported.  If a cache inconsistency is reported, however, it might be a false positive.  This
+ * happens if the server side data can be read and written non-atomically with respect to cache
+ * invalidation.
+ *
+ * @param <Query> The class used to index cache entries: must be hashable and comparable
+ * @param <Result> The class holding cache entries; use a boxed primitive if possible
+ * @hide
+ */
+@TestApi
+@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> {
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    public static abstract class QueryHandler<Q,R>
+            extends PropertyInvalidatedCache.QueryHandler<Q,R> {
+        /**
+         * Compute a result given a query.  The semantics are those of Functor.
+         */
+        public abstract @Nullable R apply(@NonNull Q query);
+
+        /**
+         * Return true if a query should not use the cache.  The default implementation
+         * always uses the cache.
+         */
+        public boolean shouldBypassCache(@NonNull Q query) {
+            return false;
+        }
+    };
+
+    /**
+     * The list of cache namespaces.  Each namespace corresponds to an sepolicy domain.  A
+     * namespace is owned by a single process, although a single process can have more
+     * than one namespace (system_server, as an example).
+     * @hide
+     */
+    @StringDef(
+        prefix = { "MODULE_"
+        },
+        value = {
+            MODULE_TEST,
+            MODULE_SYSTEM,
+            MODULE_BLUETOOTH,
+            MODULE_TELEPHONY
+        }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IpcDataCacheModule { }
+
+    /**
+     * The module used for unit tests and cts tests.  It is expected that no process in
+     * the system has permissions to write properties with this module.
+     * @hide
+     */
+    @TestApi
+    public static final String MODULE_TEST = PropertyInvalidatedCache.MODULE_TEST;
+
+    /**
+     * The module used for system server/framework caches.  This is not visible outside
+     * the system processes.
+     * @hide
+     */
+    @TestApi
+    public static final String MODULE_SYSTEM = PropertyInvalidatedCache.MODULE_SYSTEM;
+
+    /**
+     * The module used for bluetooth caches.
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    public static final String MODULE_BLUETOOTH = PropertyInvalidatedCache.MODULE_BLUETOOTH;
+
+    /**
+     * Make a new property invalidated cache.  The key is computed from the module and api
+     * parameters.
+     *
+     * @param maxEntries Maximum number of entries to cache; LRU discard
+     * @param module The module under which the cache key should be placed.
+     * @param api The api this cache front-ends.  The api must be a Java identifier but
+     * need not be an actual api.
+     * @param cacheName Name of this cache in debug and dumpsys
+     * @param computer The code to compute values that are not in the cache.
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    public IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module,
+            @NonNull String api, @NonNull String cacheName,
+            @NonNull QueryHandler<Query, Result> computer) {
+        super(maxEntries, module, api, cacheName, computer);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @Override
+    public void disableForCurrentProcess() {
+        super.disableForCurrentProcess();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    public static void disableForCurrentProcess(@NonNull String cacheName) {
+        PropertyInvalidatedCache.disableForCurrentProcess(cacheName);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @Override
+    public @Nullable Result query(@NonNull Query query) {
+        return super.query(query);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @Override
+    public void invalidateCache() {
+        super.invalidateCache();
+    }
+
+    /**
+     * Invalidate caches in all processes that are keyed for the module and api.
+     * @hide
+     */
+    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    public static void invalidateCache(@NonNull @IpcDataCacheModule String module,
+            @NonNull String api) {
+        PropertyInvalidatedCache.invalidateCache(module, api);
+    }
+
+    /**
+     * This is a convenience class that encapsulates configuration information for a
+     * cache.  It may be supplied to the cache constructors in lieu of the other
+     * parameters.  The class captures maximum entry count, the module, the key, and the
+     * api.
+     *
+     * There are three specific use cases supported by this class.
+     *
+     * 1. Instance-per-cache: create a static instance of this class using the same
+     *    parameters as would have been given to IpcDataCache (or
+     *    PropertyInvalidatedCache).  This static instance provides a hook for the
+     *    invalidateCache() and disableForLocalProcess() calls, which, generally, must
+     *    also be static.
+     *
+     * 2. Short-hand for shared configuration parameters: create an instance of this class
+     *    to capture the maximum number of entries and the module to be used by more than
+     *    one cache in the class.  Refer to this instance when creating new configs.  Only
+     *    the api and (optionally key) for the new cache must be supplied.
+     *
+     * 3. Tied caches: create a static instance of this class to capture the maximum
+     *    number of entries, the module, and the key.  Refer to this instance when
+     *    creating a new config that differs in only the api.  The new config can be
+     *    created as part of the cache constructor.  All caches that trace back to the
+     *    root config share the same key and are invalidated by the invalidateCache()
+     *    method of the root config.  All caches that trace back to the root config can be
+     *    disabled in the local process by the disableAllForCurrentProcess() method of the
+     *    root config.
+     *
+     * @hide
+     */
+    public static class Config {
+        private final int mMaxEntries;
+        @IpcDataCacheModule
+        private final String mModule;
+        private final String mApi;
+        private final String mName;
+
+        /**
+         * The list of cache names that were created extending this Config.  If
+         * disableForCurrentProcess() is invoked on this config then all children will be
+         * disabled.  Furthermore, any new children based off of this config will be
+         * disabled.  The construction order guarantees that new caches will be disabled
+         * before they are created (the Config must be created before the IpcDataCache is
+         * created).
+         */
+        private ArraySet<String> mChildren;
+
+        /**
+         * True if registered children are disabled in the current process.  If this is
+         * true then all new children are disabled as they are registered.
+         */
+        private boolean mDisabled = false;
+
+        public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
+                @NonNull String api, @NonNull String name) {
+            mMaxEntries = maxEntries;
+            mModule = module;
+            mApi = api;
+            mName = name;
+        }
+
+        /**
+         * A short-hand constructor that makes the name the same as the api.
+         */
+        public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
+                @NonNull String api) {
+            this(maxEntries, module, api, api);
+        }
+
+        /**
+         * Copy the module and max entries from the Config and take the api and name from
+         * the parameter list.
+         */
+        public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
+            this(root.maxEntries(), root.module(), api, name);
+        }
+
+        /**
+         * Copy the module and max entries from the Config and take the api and name from
+         * the parameter list.
+         */
+        public Config(@NonNull Config root, @NonNull String api) {
+            this(root.maxEntries(), root.module(), api, api);
+        }
+
+        /**
+         * Fetch a config that is a child of <this>.  The child shares the same api as the
+         * parent and is registered with the parent for the purposes of disabling in the
+         * current process.
+         */
+        public Config child(@NonNull String name) {
+            final Config result = new Config(this, api(), name);
+            registerChild(name);
+            return result;
+        }
+
+        public final int maxEntries() {
+            return mMaxEntries;
+        }
+
+        @IpcDataCacheModule
+        public final @NonNull String module() {
+            return mModule;
+        }
+
+        public final @NonNull String api() {
+            return mApi;
+        }
+
+        public final @NonNull String name() {
+            return mName;
+        }
+
+        /**
+         * Register a child cache name.  If disableForCurrentProcess() has been called
+         * against this cache, disable th new child.
+         */
+        private final void registerChild(String name) {
+            synchronized (this) {
+                if (mChildren == null) {
+                    mChildren = new ArraySet<>();
+                }
+                mChildren.add(name);
+                if (mDisabled) {
+                    IpcDataCache.disableForCurrentProcess(name);
+                }
+            }
+        }
+
+        /**
+         * Invalidate all caches that share this Config's module and api.
+         */
+        public void invalidateCache() {
+            IpcDataCache.invalidateCache(mModule, mApi);
+        }
+
+        /**
+         * Disable all caches that share this Config's name.
+         */
+        public void disableForCurrentProcess() {
+            IpcDataCache.disableForCurrentProcess(mName);
+        }
+
+        /**
+         * Disable this cache and all children.  Any child that is added in the future
+         * will alwo be disabled.
+         */
+        public void disableAllForCurrentProcess() {
+            synchronized (this) {
+                mDisabled = true;
+                disableForCurrentProcess();
+                if (mChildren != null) {
+                    for (String c : mChildren) {
+                        IpcDataCache.disableForCurrentProcess(c);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new cache using a config.
+     * @hide
+     */
+    public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
+        super(config.maxEntries(), config.module(), config.api(), config.name(), computer);
+    }
+
+    /**
+     * An interface suitable for a lambda expression instead of a QueryHandler.
+     * @hide
+     */
+    public interface RemoteCall<Query, Result> {
+        Result apply(Query query) throws RemoteException;
+    }
+
+    /**
+     * This is a query handler that is created with a lambda expression that is invoked
+     * every time the handler is called.  The handler is specifically meant for services
+     * hosted by system_server; the handler automatically rethrows RemoteException as a
+     * RuntimeException, which is the usual handling for failed binder calls.
+     */
+    private static class SystemServerCallHandler<Query, Result>
+            extends IpcDataCache.QueryHandler<Query, Result> {
+        private final RemoteCall<Query, Result> mHandler;
+        public SystemServerCallHandler(RemoteCall handler) {
+            mHandler = handler;
+        }
+        @Override
+        public Result apply(Query query) {
+            try {
+                return mHandler.apply(query);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Create a cache using a config and a lambda expression.
+     * @hide
+     */
+    public IpcDataCache(@NonNull Config config, @NonNull RemoteCall<Query, Result> computer) {
+        this(config, new SystemServerCallHandler<>(computer));
+    }
+}
diff --git a/android-34/android/os/KernelCpuThreadReaderPerfTest.java b/android-34/android/os/KernelCpuThreadReaderPerfTest.java
new file mode 100644
index 0000000..da9ed6e
--- /dev/null
+++ b/android-34/android/os/KernelCpuThreadReaderPerfTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.KernelCpuThreadReader;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Performance tests collecting per-thread CPU data.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class KernelCpuThreadReaderPerfTest {
+    @Rule
+    public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private final KernelCpuThreadReader mKernelCpuThreadReader =
+            KernelCpuThreadReader.create(8, uid -> 1000 <= uid && uid < 2000);
+
+    @Test
+    public void timeReadCurrentProcessCpuUsage() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        assertNotNull(mKernelCpuThreadReader);
+        while (state.keepRunning()) {
+            this.mKernelCpuThreadReader.getProcessCpuUsage();
+        }
+    }
+}
diff --git a/android-34/android/os/LimitExceededException.java b/android-34/android/os/LimitExceededException.java
new file mode 100644
index 0000000..d934326
--- /dev/null
+++ b/android-34/android/os/LimitExceededException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+
+/** Indicates that the app has exceeded a limit set by the System. */
+public class LimitExceededException extends IllegalStateException {
+
+    /**
+     * Constructs a new {@code LimitExceededException} with {@code null} as its
+     * detail message. The cause is not initialized, and may subsequently be
+     * initialized by a call to {@link #initCause}.
+     */
+    public LimitExceededException() {
+        super();
+    }
+
+    /**
+     * Constructs a new {@code LimitExceededException} with the specified detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     *
+     * @param message the detail message which is saved for later retrieval
+     *                by the {@link #getMessage()} method.
+     */
+    public LimitExceededException(@NonNull String message) {
+        super(message);
+    }
+}
diff --git a/android-34/android/os/LocaleList.java b/android-34/android/os/LocaleList.java
new file mode 100644
index 0000000..b74bb33
--- /dev/null
+++ b/android-34/android/os/LocaleList.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Size;
+import android.annotation.SuppressLint;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.icu.util.ULocale;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Locale;
+
+/**
+ * LocaleList is an immutable list of Locales, typically used to keep an ordered list of user
+ * preferences for locales.
+ */
+public final class LocaleList implements Parcelable {
+    private final Locale[] mList;
+    // This is a comma-separated list of the locales in the LocaleList created at construction time,
+    // basically the result of running each locale's toLanguageTag() method and concatenating them
+    // with commas in between.
+    @NonNull
+    private final String mStringRepresentation;
+
+    private static final Locale[] sEmptyList = new Locale[0];
+    private static final LocaleList sEmptyLocaleList = new LocaleList();
+
+    /**
+     * Retrieves the {@link Locale} at the specified index.
+     *
+     * @param index The position to retrieve.
+     * @return The {@link Locale} in the given index.
+     */
+    public Locale get(int index) {
+        return (0 <= index && index < mList.length) ? mList[index] : null;
+    }
+
+    /**
+     * Returns whether the {@link LocaleList} contains no {@link Locale} items.
+     *
+     * @return {@code true} if this {@link LocaleList} has no {@link Locale} items, {@code false}
+     *     otherwise.
+     */
+    public boolean isEmpty() {
+        return mList.length == 0;
+    }
+
+    /**
+     * Returns the number of {@link Locale} items in this {@link LocaleList}.
+     */
+    @IntRange(from=0)
+    public int size() {
+        return mList.length;
+    }
+
+    /**
+     * Searches this {@link LocaleList} for the specified {@link Locale} and returns the index of
+     * the first occurrence.
+     *
+     * @param locale The {@link Locale} to search for.
+     * @return The index of the first occurrence of the {@link Locale} or {@code -1} if the item
+     *     wasn't found.
+     */
+    @IntRange(from=-1)
+    public int indexOf(Locale locale) {
+        for (int i = 0; i < mList.length; i++) {
+            if (mList[i].equals(locale)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (other == this)
+            return true;
+        if (!(other instanceof LocaleList))
+            return false;
+        final Locale[] otherList = ((LocaleList) other).mList;
+        if (mList.length != otherList.length)
+            return false;
+        for (int i = 0; i < mList.length; i++) {
+            if (!mList[i].equals(otherList[i]))
+                return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        for (int i = 0; i < mList.length; i++) {
+            result = 31 * result + mList[i].hashCode();
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        for (int i = 0; i < mList.length; i++) {
+            sb.append(mList[i]);
+            if (i < mList.length - 1) {
+                sb.append(',');
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString8(mStringRepresentation);
+    }
+
+    /**
+     * Retrieves a String representation of the language tags in this list.
+     */
+    @NonNull
+    public String toLanguageTags() {
+        return mStringRepresentation;
+    }
+
+    /**
+     * Creates a new {@link LocaleList}.
+     *
+     * If two or more same locales are passed, the repeated locales will be dropped.
+     * <p>For empty lists of {@link Locale} items it is better to use {@link #getEmptyLocaleList()},
+     * which returns a pre-constructed empty list.</p>
+     *
+     * @throws NullPointerException if any of the input locales is <code>null</code>.
+     */
+    public LocaleList(@NonNull Locale... list) {
+        if (list.length == 0) {
+            mList = sEmptyList;
+            mStringRepresentation = "";
+        } else {
+            final ArrayList<Locale> localeList = new ArrayList<>();
+            final HashSet<Locale> seenLocales = new HashSet<Locale>();
+            final StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < list.length; i++) {
+                final Locale l = list[i];
+                if (l == null) {
+                    throw new NullPointerException("list[" + i + "] is null");
+                } else if (seenLocales.contains(l)) {
+                    // Dropping duplicated locale entries.
+                } else {
+                    final Locale localeClone = (Locale) l.clone();
+                    localeList.add(localeClone);
+                    sb.append(localeClone.toLanguageTag());
+                    if (i < list.length - 1) {
+                        sb.append(',');
+                    }
+                    seenLocales.add(localeClone);
+                }
+            }
+            mList = localeList.toArray(new Locale[localeList.size()]);
+            mStringRepresentation = sb.toString();
+        }
+    }
+
+    /**
+     * Constructs a locale list, with the topLocale moved to the front if it already is
+     * in otherLocales, or added to the front if it isn't.
+     *
+     * {@hide}
+     */
+    public LocaleList(@NonNull Locale topLocale, LocaleList otherLocales) {
+        if (topLocale == null) {
+            throw new NullPointerException("topLocale is null");
+        }
+
+        final int inputLength = (otherLocales == null) ? 0 : otherLocales.mList.length;
+        int topLocaleIndex = -1;
+        for (int i = 0; i < inputLength; i++) {
+            if (topLocale.equals(otherLocales.mList[i])) {
+                topLocaleIndex = i;
+                break;
+            }
+        }
+
+        final int outputLength = inputLength + (topLocaleIndex == -1 ? 1 : 0);
+        final Locale[] localeList = new Locale[outputLength];
+        localeList[0] = (Locale) topLocale.clone();
+        if (topLocaleIndex == -1) {
+            // topLocale was not in otherLocales
+            for (int i = 0; i < inputLength; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+        } else {
+            for (int i = 0; i < topLocaleIndex; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+            for (int i = topLocaleIndex + 1; i < inputLength; i++) {
+                localeList[i] = (Locale) otherLocales.mList[i].clone();
+            }
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < outputLength; i++) {
+            sb.append(localeList[i].toLanguageTag());
+            if (i < outputLength - 1) {
+                sb.append(',');
+            }
+        }
+
+        mList = localeList;
+        mStringRepresentation = sb.toString();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<LocaleList> CREATOR
+            = new Parcelable.Creator<LocaleList>() {
+        @Override
+        public LocaleList createFromParcel(Parcel source) {
+            return LocaleList.forLanguageTags(source.readString8());
+        }
+
+        @Override
+        public LocaleList[] newArray(int size) {
+            return new LocaleList[size];
+        }
+    };
+
+    /**
+     * Retrieve an empty instance of {@link LocaleList}.
+     */
+    @NonNull
+    public static LocaleList getEmptyLocaleList() {
+        return sEmptyLocaleList;
+    }
+
+    /**
+     * Generates a new LocaleList with the given language tags.
+     *
+     * @param list The language tags to be included as a single {@link String} separated by commas.
+     * @return A new instance with the {@link Locale} items identified by the given tags.
+     */
+    @NonNull
+    public static LocaleList forLanguageTags(@Nullable String list) {
+        if (list == null || list.equals("")) {
+            return getEmptyLocaleList();
+        } else {
+            final String[] tags = list.split(",");
+            final Locale[] localeArray = new Locale[tags.length];
+            for (int i = 0; i < localeArray.length; i++) {
+                localeArray[i] = Locale.forLanguageTag(tags[i]);
+            }
+            return new LocaleList(localeArray);
+        }
+    }
+
+    private static String getLikelyScript(Locale locale) {
+        final String script = locale.getScript();
+        if (!script.isEmpty()) {
+            return script;
+        } else {
+            // TODO: Cache the results if this proves to be too slow
+            return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript();
+        }
+    }
+
+    private static final String STRING_EN_XA = "en-XA";
+    private static final String STRING_AR_XB = "ar-XB";
+    private static final Locale LOCALE_EN_XA = new Locale("en", "XA");
+    private static final Locale LOCALE_AR_XB = new Locale("ar", "XB");
+    private static final int NUM_PSEUDO_LOCALES = 2;
+
+    private static boolean isPseudoLocale(String locale) {
+        return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale);
+    }
+
+    /**
+     * Returns true if locale is a pseudo-locale, false otherwise.
+     * {@hide}
+     */
+    public static boolean isPseudoLocale(Locale locale) {
+        return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
+    }
+
+    /**
+     * Returns true if locale is a pseudo-locale, false otherwise.
+     */
+    public static boolean isPseudoLocale(@Nullable ULocale locale) {
+        return isPseudoLocale(locale != null ? locale.toLocale() : null);
+    }
+
+    /**
+     * Determine whether two locales are considered a match, even if they are not exactly equal.
+     * They are considered as a match when both of their languages and scripts
+     * (explicit or inferred) are identical. This means that a user would be able to understand
+     * the content written in the supported locale even if they say they prefer the desired locale.
+     *
+     * E.g. [zh-HK] matches [zh-Hant]; [en-US] matches [en-CA]
+     *
+     * @param supported The supported {@link Locale} to be compared.
+     * @param desired   The desired {@link Locale} to be compared.
+     * @return True if they match, false otherwise.
+     */
+    public static boolean matchesLanguageAndScript(@SuppressLint("UseIcu") @NonNull
+            Locale supported, @SuppressLint("UseIcu") @NonNull Locale desired) {
+        if (supported.equals(desired)) {
+            return true;  // return early so we don't do unnecessary computation
+        }
+        if (!supported.getLanguage().equals(desired.getLanguage())) {
+            return false;
+        }
+        if (isPseudoLocale(supported) || isPseudoLocale(desired)) {
+            // The locales are not the same, but the languages are the same, and one of the locales
+            // is a pseudo-locale. So this is not a match.
+            return false;
+        }
+        final String supportedScr = getLikelyScript(supported);
+        if (supportedScr.isEmpty()) {
+            // If we can't guess a script, we don't know enough about the locales' language to find
+            // if the locales match. So we fall back to old behavior of matching, which considered
+            // locales with different regions different.
+            final String supportedRegion = supported.getCountry();
+            return supportedRegion.isEmpty() || supportedRegion.equals(desired.getCountry());
+        }
+        final String desiredScr = getLikelyScript(desired);
+        // There is no match if the two locales use different scripts. This will most imporantly
+        // take care of traditional vs simplified Chinese.
+        return supportedScr.equals(desiredScr);
+    }
+
+    private int findFirstMatchIndex(Locale supportedLocale) {
+        for (int idx = 0; idx < mList.length; idx++) {
+            if (matchesLanguageAndScript(supportedLocale, mList[idx])) {
+                return idx;
+            }
+        }
+        return Integer.MAX_VALUE;
+    }
+
+    private static final Locale EN_LATN = Locale.forLanguageTag("en-Latn");
+
+    private int computeFirstMatchIndex(Collection<String> supportedLocales,
+            boolean assumeEnglishIsSupported) {
+        if (mList.length == 1) {  // just one locale, perhaps the most common scenario
+            return 0;
+        }
+        if (mList.length == 0) {  // empty locale list
+            return -1;
+        }
+
+        int bestIndex = Integer.MAX_VALUE;
+        // Try English first, so we can return early if it's in the LocaleList
+        if (assumeEnglishIsSupported) {
+            final int idx = findFirstMatchIndex(EN_LATN);
+            if (idx == 0) { // We have a match on the first locale, which is good enough
+                return 0;
+            } else if (idx < bestIndex) {
+                bestIndex = idx;
+            }
+        }
+        for (String languageTag : supportedLocales) {
+            final Locale supportedLocale = Locale.forLanguageTag(languageTag);
+            // We expect the average length of locale lists used for locale resolution to be
+            // smaller than three, so it's OK to do this as an O(mn) algorithm.
+            final int idx = findFirstMatchIndex(supportedLocale);
+            if (idx == 0) { // We have a match on the first locale, which is good enough
+                return 0;
+            } else if (idx < bestIndex) {
+                bestIndex = idx;
+            }
+        }
+        if (bestIndex == Integer.MAX_VALUE) {
+            // no match was found, so we fall back to the first locale in the locale list
+            return 0;
+        } else {
+            return bestIndex;
+        }
+    }
+
+    private Locale computeFirstMatch(Collection<String> supportedLocales,
+            boolean assumeEnglishIsSupported) {
+        int bestIndex = computeFirstMatchIndex(supportedLocales, assumeEnglishIsSupported);
+        return bestIndex == -1 ? null : mList[bestIndex];
+    }
+
+    /**
+     * Returns the first match in the locale list given an unordered array of supported locales
+     * in BCP 47 format.
+     *
+     * @return The first {@link Locale} from this list that appears in the given array, or
+     *     {@code null} if the {@link LocaleList} is empty.
+     */
+    @Nullable
+    public Locale getFirstMatch(String[] supportedLocales) {
+        return computeFirstMatch(Arrays.asList(supportedLocales),
+                false /* assume English is not supported */);
+    }
+
+    /**
+     * {@hide}
+     */
+    public int getFirstMatchIndex(String[] supportedLocales) {
+        return computeFirstMatchIndex(Arrays.asList(supportedLocales),
+                false /* assume English is not supported */);
+    }
+
+    /**
+     * Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
+     * {@hide}
+     */
+    @Nullable
+    public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) {
+        return computeFirstMatch(Arrays.asList(supportedLocales),
+                true /* assume English is supported */);
+    }
+
+    /**
+     * {@hide}
+     */
+    public int getFirstMatchIndexWithEnglishSupported(Collection<String> supportedLocales) {
+        return computeFirstMatchIndex(supportedLocales, true /* assume English is supported */);
+    }
+
+    /**
+     * {@hide}
+     */
+    public int getFirstMatchIndexWithEnglishSupported(String[] supportedLocales) {
+        return getFirstMatchIndexWithEnglishSupported(Arrays.asList(supportedLocales));
+    }
+
+    /**
+     * Returns true if the collection of locale tags only contains empty locales and pseudolocales.
+     * Assumes that there is no repetition in the input.
+     * {@hide}
+     */
+    public static boolean isPseudoLocalesOnly(@Nullable String[] supportedLocales) {
+        if (supportedLocales == null) {
+            return true;
+        }
+
+        if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
+            // This is for optimization. Since there's no repetition in the input, if we have more
+            // than the number of pseudo-locales plus one for the empty string, it's guaranteed
+            // that we have some meaninful locale in the collection, so the list is not "practically
+            // empty".
+            return false;
+        }
+        for (String locale : supportedLocales) {
+            if (!locale.isEmpty() && !isPseudoLocale(locale)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private final static Object sLock = new Object();
+
+    @GuardedBy("sLock")
+    private static LocaleList sLastExplicitlySetLocaleList = null;
+    @GuardedBy("sLock")
+    private static LocaleList sDefaultLocaleList = null;
+    @GuardedBy("sLock")
+    private static LocaleList sDefaultAdjustedLocaleList = null;
+    @GuardedBy("sLock")
+    private static Locale sLastDefaultLocale = null;
+
+    /**
+     * The result is guaranteed to include the default Locale returned by Locale.getDefault(), but
+     * not necessarily at the top of the list. The default locale not being at the top of the list
+     * is an indication that the system has set the default locale to one of the user's other
+     * preferred locales, having concluded that the primary preference is not supported but a
+     * secondary preference is.
+     *
+     * <p>Note that the default LocaleList would change if Locale.setDefault() is called. This
+     * method takes that into account by always checking the output of Locale.getDefault() and
+     * recalculating the default LocaleList if needed.</p>
+     */
+    @NonNull @Size(min=1)
+    public static LocaleList getDefault() {
+        final Locale defaultLocale = Locale.getDefault();
+        synchronized (sLock) {
+            if (!defaultLocale.equals(sLastDefaultLocale)) {
+                sLastDefaultLocale = defaultLocale;
+                // It's either the first time someone has asked for the default locale list, or
+                // someone has called Locale.setDefault() since we last set or adjusted the default
+                // locale list. So let's recalculate the locale list.
+                if (sDefaultLocaleList != null
+                        && defaultLocale.equals(sDefaultLocaleList.get(0))) {
+                    // The default Locale has changed, but it happens to be the first locale in the
+                    // default locale list, so we don't need to construct a new locale list.
+                    return sDefaultLocaleList;
+                }
+                sDefaultLocaleList = new LocaleList(defaultLocale, sLastExplicitlySetLocaleList);
+                sDefaultAdjustedLocaleList = sDefaultLocaleList;
+            }
+            // sDefaultLocaleList can't be null, since it can't be set to null by
+            // LocaleList.setDefault(), and if getDefault() is called before a call to
+            // setDefault(), sLastDefaultLocale would be null and the check above would set
+            // sDefaultLocaleList.
+            return sDefaultLocaleList;
+        }
+    }
+
+    /**
+     * Returns the default locale list, adjusted by moving the default locale to its first
+     * position.
+     */
+    @NonNull @Size(min=1)
+    public static LocaleList getAdjustedDefault() {
+        getDefault(); // to recalculate the default locale list, if necessary
+        synchronized (sLock) {
+            return sDefaultAdjustedLocaleList;
+        }
+    }
+
+    /**
+     * Also sets the default locale by calling Locale.setDefault() with the first locale in the
+     * list.
+     *
+     * @throws NullPointerException if the input is <code>null</code>.
+     * @throws IllegalArgumentException if the input is empty.
+     */
+    public static void setDefault(@NonNull @Size(min=1) LocaleList locales) {
+        setDefault(locales, 0);
+    }
+
+    /**
+     * This may be used directly by system processes to set the default locale list for apps. For
+     * such uses, the default locale list would always come from the user preferences, but the
+     * default locale may have been chosen to be a locale other than the first locale in the locale
+     * list (based on the locales the app supports).
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static void setDefault(@NonNull @Size(min=1) LocaleList locales, int localeIndex) {
+        if (locales == null) {
+            throw new NullPointerException("locales is null");
+        }
+        if (locales.isEmpty()) {
+            throw new IllegalArgumentException("locales is empty");
+        }
+        synchronized (sLock) {
+            sLastDefaultLocale = locales.get(localeIndex);
+            Locale.setDefault(sLastDefaultLocale);
+            sLastExplicitlySetLocaleList = locales;
+            sDefaultLocaleList = locales;
+            if (localeIndex == 0) {
+                sDefaultAdjustedLocaleList = sDefaultLocaleList;
+            } else {
+                sDefaultAdjustedLocaleList = new LocaleList(
+                        sLastDefaultLocale, sDefaultLocaleList);
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/LongArrayMultiStateCounterPerfTest.java b/android-34/android/os/LongArrayMultiStateCounterPerfTest.java
new file mode 100644
index 0000000..8d2d044
--- /dev/null
+++ b/android-34/android/os/LongArrayMultiStateCounterPerfTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.LongArrayMultiStateCounter;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LongArrayMultiStateCounterPerfTest {
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * A complete line-for-line reimplementation of
+     * {@link com.android.internal.os.LongArrayMultiStateCounter}, only in Java instead of
+     * native.
+     */
+    private static class TestLongArrayMultiStateCounter {
+        private final int mStateCount;
+        private final int mArrayLength;
+        private int mCurrentState;
+        private long mLastStateChangeTimestampMs = -1;
+        private long mLastUpdateTimestampMs = -1;
+
+        private static class State {
+            private long mTimeInStateSinceUpdate;
+            private long[] mCounter;
+        }
+
+        private final State[] mStates;
+        private final long[] mLastTimeInFreq;
+        private final long[] mDelta;
+
+        TestLongArrayMultiStateCounter(int stateCount, int arrayLength) {
+            mStateCount = stateCount;
+            mArrayLength = arrayLength;
+            mStates = new State[stateCount];
+            for (int i = 0; i < mStateCount; i++) {
+                mStates[i] = new State();
+                mStates[i].mCounter = new long[mArrayLength];
+            }
+            mLastTimeInFreq = new long[mArrayLength];
+            mDelta = new long[mArrayLength];
+        }
+
+        public void setState(int state, long timestampMs) {
+            if (mLastStateChangeTimestampMs > 0) {
+                if (timestampMs >= mLastStateChangeTimestampMs) {
+                    mStates[mCurrentState].mTimeInStateSinceUpdate +=
+                            timestampMs - mLastStateChangeTimestampMs;
+                } else {
+                    for (int i = 0; i < mStateCount; i++) {
+                        mStates[i].mTimeInStateSinceUpdate = 0;
+                    }
+                }
+            }
+            mCurrentState = state;
+            mLastStateChangeTimestampMs = timestampMs;
+        }
+
+        public void updateValue(long[] timeInFreq, long timestampMs) {
+            setState(mCurrentState, timestampMs);
+
+            if (mLastUpdateTimestampMs >= 0) {
+                if (timestampMs > mLastUpdateTimestampMs) {
+                    if (delta(mLastTimeInFreq, timeInFreq, mDelta)) {
+                        long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs;
+                        for (int i = 0; i < mStateCount; i++) {
+                            long timeInState = mStates[i].mTimeInStateSinceUpdate;
+                            if (timeInState > 0) {
+                                add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate);
+                                mStates[i].mTimeInStateSinceUpdate = 0;
+                            }
+                        }
+                    } else {
+                        throw new RuntimeException();
+                    }
+                } else if (timestampMs < mLastUpdateTimestampMs) {
+                    throw new RuntimeException();
+                }
+            }
+            System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength);
+            mLastUpdateTimestampMs = timestampMs;
+        }
+
+        private boolean delta(long[] timeInFreq1, long[] timeInFreq2, long[] delta) {
+            if (delta.length != mArrayLength) {
+                throw new RuntimeException();
+            }
+
+            boolean is_delta_valid = true;
+            for (int i = 0; i < mStateCount; i++) {
+                if (timeInFreq2[i] >= timeInFreq1[i]) {
+                    delta[i] = timeInFreq2[i] - timeInFreq1[i];
+                } else {
+                    delta[i] = 0;
+                    is_delta_valid = false;
+                }
+            }
+
+            return is_delta_valid;
+        }
+
+        private void add(long[] counter, long[] delta, long numerator, long denominator) {
+            if (numerator != denominator) {
+                for (int i = 0; i < mArrayLength; i++) {
+                    counter[i] += delta[i] * numerator / denominator;
+                }
+            } else {
+                for (int i = 0; i < mArrayLength; i++) {
+                    counter[i] += delta[i];
+                }
+            }
+        }
+    }
+
+    @Test
+    public void javaImplementation() {
+        TestLongArrayMultiStateCounter counter =
+                new TestLongArrayMultiStateCounter(2, 4);
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        long time = 1000;
+        long[] timeInFreq = {100, 200, 300, 400};
+        while (state.keepRunning()) {
+            counter.setState(1, time);
+            counter.setState(0, time + 1000);
+            counter.updateValue(timeInFreq, time + 2000);
+            time += 10000;
+        }
+    }
+
+    @Test
+    public void nativeImplementation() {
+        LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        long time = 1000;
+        LongArrayMultiStateCounter.LongArrayContainer timeInFreq =
+                new LongArrayMultiStateCounter.LongArrayContainer(4);
+        timeInFreq.setValues(new long[]{100, 200, 300, 400});
+        while (state.keepRunning()) {
+            counter.setState(1, time);
+            counter.setState(0, time + 1000);
+            counter.updateValues(timeInFreq, time + 2000);
+            time += 10000;
+        }
+    }
+}
diff --git a/android-34/android/os/Looper.java b/android-34/android/os/Looper.java
new file mode 100644
index 0000000..712d328
--- /dev/null
+++ b/android-34/android/os/Looper.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
+import android.util.Printer;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+/**
+  * Class used to run a message loop for a thread.  Threads by default do
+  * not have a message loop associated with them; to create one, call
+  * {@link #prepare} in the thread that is to run the loop, and then
+  * {@link #loop} to have it process messages until the loop is stopped.
+  *
+  * <p>Most interaction with a message loop is through the
+  * {@link Handler} class.
+  *
+  * <p>This is a typical example of the implementation of a Looper thread,
+  * using the separation of {@link #prepare} and {@link #loop} to create an
+  * initial Handler to communicate with the Looper.
+  *
+  * <pre>
+  *  class LooperThread extends Thread {
+  *      public Handler mHandler;
+  *
+  *      public void run() {
+  *          Looper.prepare();
+  *
+  *          mHandler = new Handler(Looper.myLooper()) {
+  *              public void handleMessage(Message msg) {
+  *                  // process incoming messages here
+  *              }
+  *          };
+  *
+  *          Looper.loop();
+  *      }
+  *  }</pre>
+  */
+public final class Looper {
+    /*
+     * API Implementation Note:
+     *
+     * This class contains the code required to set up and manage an event loop
+     * based on MessageQueue.  APIs that affect the state of the queue should be
+     * defined on MessageQueue or Handler rather than on Looper itself.  For example,
+     * idle handlers and sync barriers are defined on the queue whereas preparing the
+     * thread, looping, and quitting are defined on the looper.
+     */
+
+    private static final String TAG = "Looper";
+
+    // sThreadLocal.get() will return null unless you've called prepare().
+    @UnsupportedAppUsage
+    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
+    @UnsupportedAppUsage
+    private static Looper sMainLooper;  // guarded by Looper.class
+    private static Observer sObserver;
+
+    @UnsupportedAppUsage
+    final MessageQueue mQueue;
+    final Thread mThread;
+    private boolean mInLoop;
+
+    @UnsupportedAppUsage
+    private Printer mLogging;
+    private long mTraceTag;
+
+    /**
+     * If set, the looper will show a warning log if a message dispatch takes longer than this.
+     */
+    private long mSlowDispatchThresholdMs;
+
+    /**
+     * If set, the looper will show a warning log if a message delivery (actual delivery time -
+     * post time) takes longer than this.
+     */
+    private long mSlowDeliveryThresholdMs;
+
+    /**
+     * True if a message delivery takes longer than {@link #mSlowDeliveryThresholdMs}.
+     */
+    private boolean mSlowDeliveryDetected;
+
+    /** Initialize the current thread as a looper.
+      * This gives you a chance to create handlers that then reference
+      * this looper, before actually starting the loop. Be sure to call
+      * {@link #loop()} after calling this method, and end it by calling
+      * {@link #quit()}.
+      */
+    public static void prepare() {
+        prepare(true);
+    }
+
+    private static void prepare(boolean quitAllowed) {
+        if (sThreadLocal.get() != null) {
+            throw new RuntimeException("Only one Looper may be created per thread");
+        }
+        sThreadLocal.set(new Looper(quitAllowed));
+    }
+
+    /**
+     * Initialize the current thread as a looper, marking it as an
+     * application's main looper. See also: {@link #prepare()}
+     *
+     * @deprecated The main looper for your application is created by the Android environment,
+     *   so you should never need to call this function yourself.
+     */
+    @Deprecated
+    public static void prepareMainLooper() {
+        prepare(false);
+        synchronized (Looper.class) {
+            if (sMainLooper != null) {
+                throw new IllegalStateException("The main Looper has already been prepared.");
+            }
+            sMainLooper = myLooper();
+        }
+    }
+
+    /**
+     * Returns the application's main looper, which lives in the main thread of the application.
+     */
+    public static Looper getMainLooper() {
+        synchronized (Looper.class) {
+            return sMainLooper;
+        }
+    }
+
+    /**
+     * Set the transaction observer for all Loopers in this process.
+     *
+     * @hide
+     */
+    public static void setObserver(@Nullable Observer observer) {
+        sObserver = observer;
+    }
+
+    /**
+     * Poll and deliver single message, return true if the outer loop should continue.
+     */
+    @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
+            "ClearIdentityCallNotFollowedByTryFinally"})
+    private static boolean loopOnce(final Looper me,
+            final long ident, final int thresholdOverride) {
+        Message msg = me.mQueue.next(); // might block
+        if (msg == null) {
+            // No message indicates that the message queue is quitting.
+            return false;
+        }
+
+        // This must be in a local variable, in case a UI event sets the logger
+        final Printer logging = me.mLogging;
+        if (logging != null) {
+            logging.println(">>>>> Dispatching to " + msg.target + " "
+                    + msg.callback + ": " + msg.what);
+        }
+        // Make sure the observer won't change while processing a transaction.
+        final Observer observer = sObserver;
+
+        final long traceTag = me.mTraceTag;
+        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
+        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
+
+        final boolean hasOverride = thresholdOverride >= 0;
+        if (hasOverride) {
+            slowDispatchThresholdMs = thresholdOverride;
+            slowDeliveryThresholdMs = thresholdOverride;
+        }
+        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0 || hasOverride)
+                && (msg.when > 0);
+        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0 || hasOverride);
+
+        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
+        final boolean needEndTime = logSlowDispatch;
+
+        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
+            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
+        }
+
+        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
+        final long dispatchEnd;
+        Object token = null;
+        if (observer != null) {
+            token = observer.messageDispatchStarting();
+        }
+        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
+        try {
+            msg.target.dispatchMessage(msg);
+            if (observer != null) {
+                observer.messageDispatched(token, msg);
+            }
+            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
+        } catch (Exception exception) {
+            if (observer != null) {
+                observer.dispatchingThrewException(token, msg, exception);
+            }
+            throw exception;
+        } finally {
+            ThreadLocalWorkSource.restore(origWorkSource);
+            if (traceTag != 0) {
+                Trace.traceEnd(traceTag);
+            }
+        }
+        if (logSlowDelivery) {
+            if (me.mSlowDeliveryDetected) {
+                if ((dispatchStart - msg.when) <= 10) {
+                    Slog.w(TAG, "Drained");
+                    me.mSlowDeliveryDetected = false;
+                }
+            } else {
+                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
+                        msg)) {
+                    // Once we write a slow delivery log, suppress until the queue drains.
+                    me.mSlowDeliveryDetected = true;
+                }
+            }
+        }
+        if (logSlowDispatch) {
+            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
+        }
+
+        if (logging != null) {
+            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
+        }
+
+        // Make sure that during the course of dispatching the
+        // identity of the thread wasn't corrupted.
+        final long newIdent = Binder.clearCallingIdentity();
+        if (ident != newIdent) {
+            Log.wtf(TAG, "Thread identity changed from 0x"
+                    + Long.toHexString(ident) + " to 0x"
+                    + Long.toHexString(newIdent) + " while dispatching to "
+                    + msg.target.getClass().getName() + " "
+                    + msg.callback + " what=" + msg.what);
+        }
+
+        msg.recycleUnchecked();
+
+        return true;
+    }
+
+    /**
+     * Run the message queue in this thread. Be sure to call
+     * {@link #quit()} to end the loop.
+     */
+    @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
+            "ClearIdentityCallNotFollowedByTryFinally",
+            "ResultOfClearIdentityCallNotStoredInVariable"})
+    public static void loop() {
+        final Looper me = myLooper();
+        if (me == null) {
+            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
+        }
+        if (me.mInLoop) {
+            Slog.w(TAG, "Loop again would have the queued messages be executed"
+                    + " before this one completed.");
+        }
+
+        me.mInLoop = true;
+
+        // Make sure the identity of this thread is that of the local process,
+        // and keep track of what that identity token actually is.
+        Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
+
+        // Allow overriding a threshold with a system prop. e.g.
+        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
+        final int thresholdOverride =
+                SystemProperties.getInt("log.looper."
+                        + Process.myUid() + "."
+                        + Thread.currentThread().getName()
+                        + ".slow", -1);
+
+        me.mSlowDeliveryDetected = false;
+
+        for (;;) {
+            if (!loopOnce(me, ident, thresholdOverride)) {
+                return;
+            }
+        }
+    }
+
+    private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
+            String what, Message msg) {
+        final long actualTime = measureEnd - measureStart;
+        if (actualTime < threshold) {
+            return false;
+        }
+        // For slow delivery, the current message isn't really important, but log it anyway.
+        Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
+                + Thread.currentThread().getName() + " h="
+                + msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
+        return true;
+    }
+
+    /**
+     * Return the Looper object associated with the current thread.  Returns
+     * null if the calling thread is not associated with a Looper.
+     */
+    public static @Nullable Looper myLooper() {
+        return sThreadLocal.get();
+    }
+
+    /**
+     * Return the {@link MessageQueue} object associated with the current
+     * thread.  This must be called from a thread running a Looper, or a
+     * NullPointerException will be thrown.
+     */
+    public static @NonNull MessageQueue myQueue() {
+        return myLooper().mQueue;
+    }
+
+    private Looper(boolean quitAllowed) {
+        mQueue = new MessageQueue(quitAllowed);
+        mThread = Thread.currentThread();
+    }
+
+    /**
+     * Returns true if the current thread is this looper's thread.
+     */
+    public boolean isCurrentThread() {
+        return Thread.currentThread() == mThread;
+    }
+
+    /**
+     * Control logging of messages as they are processed by this Looper.  If
+     * enabled, a log message will be written to <var>printer</var>
+     * at the beginning and ending of each message dispatch, identifying the
+     * target Handler and message contents.
+     *
+     * @param printer A Printer object that will receive log messages, or
+     * null to disable message logging.
+     */
+    public void setMessageLogging(@Nullable Printer printer) {
+        mLogging = printer;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public void setTraceTag(long traceTag) {
+        mTraceTag = traceTag;
+    }
+
+    /**
+     * Set a thresholds for slow dispatch/delivery log.
+     * {@hide}
+     */
+    public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
+        mSlowDispatchThresholdMs = slowDispatchThresholdMs;
+        mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
+    }
+
+    /**
+     * Quits the looper.
+     * <p>
+     * Causes the {@link #loop} method to terminate without processing any
+     * more messages in the message queue.
+     * </p><p>
+     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+     * For example, the {@link Handler#sendMessage(Message)} method will return false.
+     * </p><p class="note">
+     * Using this method may be unsafe because some messages may not be delivered
+     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
+     * that all pending work is completed in an orderly manner.
+     * </p>
+     *
+     * @see #quitSafely
+     */
+    public void quit() {
+        mQueue.quit(false);
+    }
+
+    /**
+     * Quits the looper safely.
+     * <p>
+     * Causes the {@link #loop} method to terminate as soon as all remaining messages
+     * in the message queue that are already due to be delivered have been handled.
+     * However pending delayed messages with due times in the future will not be
+     * delivered before the loop terminates.
+     * </p><p>
+     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
+     * For example, the {@link Handler#sendMessage(Message)} method will return false.
+     * </p>
+     */
+    public void quitSafely() {
+        mQueue.quit(true);
+    }
+
+    /**
+     * Gets the Thread associated with this Looper.
+     *
+     * @return The looper's thread.
+     */
+    public @NonNull Thread getThread() {
+        return mThread;
+    }
+
+    /**
+     * Gets this looper's message queue.
+     *
+     * @return The looper's message queue.
+     */
+    public @NonNull MessageQueue getQueue() {
+        return mQueue;
+    }
+
+    /**
+     * Dumps the state of the looper for debugging purposes.
+     *
+     * @param pw A printer to receive the contents of the dump.
+     * @param prefix A prefix to prepend to each line which is printed.
+     */
+    public void dump(@NonNull Printer pw, @NonNull String prefix) {
+        pw.println(prefix + toString());
+        mQueue.dump(pw, prefix + "  ", null);
+    }
+
+    /**
+     * Dumps the state of the looper for debugging purposes.
+     *
+     * @param pw A printer to receive the contents of the dump.
+     * @param prefix A prefix to prepend to each line which is printed.
+     * @param handler Only dump messages for this Handler.
+     * @hide
+     */
+    public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
+        pw.println(prefix + toString());
+        mQueue.dump(pw, prefix + "  ", handler);
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long looperToken = proto.start(fieldId);
+        proto.write(LooperProto.THREAD_NAME, mThread.getName());
+        proto.write(LooperProto.THREAD_ID, mThread.getId());
+        if (mQueue != null) {
+            mQueue.dumpDebug(proto, LooperProto.QUEUE);
+        }
+        proto.end(looperToken);
+    }
+
+    @Override
+    public String toString() {
+        return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+                + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
+    }
+
+    /** {@hide} */
+    public interface Observer {
+        /**
+         * Called right before a message is dispatched.
+         *
+         * <p> The token type is not specified to allow the implementation to specify its own type.
+         *
+         * @return a token used for collecting telemetry when dispatching a single message.
+         *         The token token must be passed back exactly once to either
+         *         {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException}
+         *         and must not be reused again.
+         *
+         */
+        Object messageDispatchStarting();
+
+        /**
+         * Called when a message was processed by a Handler.
+         *
+         * @param token Token obtained by previously calling
+         *              {@link Observer#messageDispatchStarting} on the same Observer instance.
+         * @param msg The message that was dispatched.
+         */
+        void messageDispatched(Object token, Message msg);
+
+        /**
+         * Called when an exception was thrown while processing a message.
+         *
+         * @param token Token obtained by previously calling
+         *              {@link Observer#messageDispatchStarting} on the same Observer instance.
+         * @param msg The message that was dispatched and caused an exception.
+         * @param exception The exception that was thrown.
+         */
+        void dispatchingThrewException(Object token, Message msg, Exception exception);
+    }
+}
diff --git a/android-34/android/os/LooperStatsPerfTest.java b/android-34/android/os/LooperStatsPerfTest.java
new file mode 100644
index 0000000..162167d
--- /dev/null
+++ b/android-34/android/os/LooperStatsPerfTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.os.LooperStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Performance tests for {@link LooperStats}.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LooperStatsPerfTest {
+    private static final int DISTINCT_MESSAGE_COUNT = 1000;
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    private LooperStats mStats;
+    private CachedDeviceState mDeviceState;
+    private HandlerThread mThread;
+    private Message[] mMessages = new Message[DISTINCT_MESSAGE_COUNT];
+
+    @Before
+    public void setUp() {
+        mStats = new LooperStats(1, DISTINCT_MESSAGE_COUNT - 1);
+        mDeviceState = new CachedDeviceState(false, false);
+        mStats.setDeviceState(mDeviceState.getReadonlyClient());
+        // The tests are all single-threaded. HandlerThread is created to allow creating Handlers.
+        mThread = new HandlerThread("UnusedThread");
+        mThread.start();
+        for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+            mMessages[i] = mThread.getThreadHandler().obtainMessage(i);
+        }
+    }
+
+    @After
+    public void tearDown() {
+        mThread.quit();
+    }
+
+    @Test
+    public void timeHundredPercentSampling() {
+        mStats.setSamplingInterval(1);
+        runScenario();
+    }
+
+    @Test
+    public void timeOnePercentSampling() {
+        mStats.setSamplingInterval(100);
+        runScenario();
+    }
+
+    @Test
+    public void timeCollectionDisabled() {
+        // We do not collect data on charger.
+        mDeviceState.setCharging(true);
+        runScenario();
+    }
+
+    private void runScenario() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+                Object token = mStats.messageDispatchStarting();
+                mStats.messageDispatched(token, mMessages[i]);
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/MemoryFile.java b/android-34/android/os/MemoryFile.java
new file mode 100644
index 0000000..95337f6
--- /dev/null
+++ b/android-34/android/os/MemoryFile.java
@@ -0,0 +1,336 @@
+/*
+ * 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.
+ * 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.system.ErrnoException;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+
+/**
+ * MemoryFile is a wrapper for {@link SharedMemory} which can optionally be set to purgeable.
+ *
+ * Applications should generally prefer to use {@link SharedMemory} which offers more flexible
+ * access & control over the shared memory region than MemoryFile does.
+ *
+ * Purgeable files may have their contents reclaimed by the kernel
+ * in low memory conditions (only if allowPurging is set to true).
+ * After a file is purged, attempts to read or write the file will
+ * cause an IOException to be thrown.
+ */
+public class MemoryFile {
+    private static String TAG = "MemoryFile";
+
+    // Returns 'true' if purged, 'false' otherwise
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private static native boolean native_pin(FileDescriptor fd, boolean pin) throws IOException;
+    @UnsupportedAppUsage
+    private static native int native_get_size(FileDescriptor fd) throws IOException;
+
+    private SharedMemory mSharedMemory;
+    private ByteBuffer mMapping;
+    private boolean mAllowPurging = false;  // true if our ashmem region is unpinned
+
+    /**
+     * Allocates a new ashmem region. The region is initially not purgable.
+     *
+     * @param name optional name for the file (can be null).
+     * @param length of the memory file in bytes, must be positive.
+     * @throws IOException if the memory file could not be created.
+     */
+    public MemoryFile(String name, int length) throws IOException {
+        try {
+            mSharedMemory = SharedMemory.create(name, length);
+            mMapping = mSharedMemory.mapReadWrite();
+        } catch (ErrnoException ex) {
+            ex.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Closes the memory file. If there are no other open references to the memory
+     * file, it will be deleted.
+     */
+    public void close() {
+        deactivate();
+        mSharedMemory.close();
+    }
+
+    /**
+     * Unmaps the memory file from the process's memory space, but does not close it.
+     * After this method has been called, read and write operations through this object
+     * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    void deactivate() {
+        if (mMapping != null) {
+            SharedMemory.unmap(mMapping);
+            mMapping = null;
+        }
+    }
+
+    private void checkActive() throws IOException {
+        if (mMapping == null) {
+            throw new IOException("MemoryFile has been deactivated");
+        }
+    }
+
+    private void beginAccess() throws IOException {
+        checkActive();
+        if (mAllowPurging) {
+            if (native_pin(mSharedMemory.getFileDescriptor(), true)) {
+                throw new IOException("MemoryFile has been purged");
+            }
+        }
+    }
+
+    private void endAccess() throws IOException {
+        if (mAllowPurging) {
+            native_pin(mSharedMemory.getFileDescriptor(), false);
+        }
+    }
+
+    /**
+     * Returns the length of the memory file.
+     *
+     * @return file length.
+     */
+    public int length() {
+        return mSharedMemory.getSize();
+    }
+
+    /**
+     * Is memory file purging enabled?
+     *
+     * @return true if the file may be purged.
+     *
+     * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
+     * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
+     * to react to memory events and release shared memory regions as appropriate.
+     */
+    @Deprecated
+    public boolean isPurgingAllowed() {
+        return mAllowPurging;
+    }
+
+    /**
+     * Enables or disables purging of the memory file.
+     *
+     * @param allowPurging true if the operating system can purge the contents
+     * of the file in low memory situations
+     * @return previous value of allowPurging
+     *
+     * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
+     * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
+     * to react to memory events and release shared memory regions as appropriate.
+     */
+    @Deprecated
+    synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
+        boolean oldValue = mAllowPurging;
+        if (oldValue != allowPurging) {
+            native_pin(mSharedMemory.getFileDescriptor(), !allowPurging);
+            mAllowPurging = allowPurging;
+        }
+        return oldValue;
+    }
+
+    /**
+     * Creates a new InputStream for reading from the memory file.
+     *
+     @return InputStream
+     */
+    public InputStream getInputStream() {
+        return new MemoryInputStream();
+    }
+
+    /**
+     * Creates a new OutputStream for writing to the memory file.
+     *
+     @return OutputStream
+     */
+     public OutputStream getOutputStream() {
+        return new MemoryOutputStream();
+    }
+
+    /**
+     * Reads bytes from the memory file.
+     * Will throw an IOException if the file has been purged.
+     *
+     * @param buffer byte array to read bytes into.
+     * @param srcOffset offset into the memory file to read from.
+     * @param destOffset offset into the byte array buffer to read into.
+     * @param count number of bytes to read.
+     * @return number of bytes read.
+     * @throws IOException if the memory file has been purged or deactivated.
+     */
+    public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
+            throws IOException {
+        beginAccess();
+        try {
+            mMapping.position(srcOffset);
+            mMapping.get(buffer, destOffset, count);
+        } finally {
+            endAccess();
+        }
+        return count;
+    }
+
+    /**
+     * Write bytes to the memory file.
+     * Will throw an IOException if the file has been purged.
+     *
+     * @param buffer byte array to write bytes from.
+     * @param srcOffset offset into the byte array buffer to write from.
+     * @param destOffset offset  into the memory file to write to.
+     * @param count number of bytes to write.
+     * @throws IOException if the memory file has been purged or deactivated.
+     */
+    public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
+            throws IOException {
+        beginAccess();
+        try {
+            mMapping.position(destOffset);
+            mMapping.put(buffer, srcOffset, count);
+        } finally {
+            endAccess();
+        }
+    }
+
+    /**
+     * Gets a FileDescriptor for the memory file.
+     *
+     * The returned file descriptor is not duplicated.
+     *
+     * @throws IOException If the memory file has been closed.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public FileDescriptor getFileDescriptor() throws IOException {
+        return mSharedMemory.getFileDescriptor();
+    }
+
+    /**
+     * Returns the size of the memory file that the file descriptor refers to,
+     * or -1 if the file descriptor does not refer to a memory file.
+     *
+     * @throws IOException If <code>fd</code> is not a valid file descriptor.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static int getSize(FileDescriptor fd) throws IOException {
+        return native_get_size(fd);
+    }
+
+    private class MemoryInputStream extends InputStream {
+
+        private int mMark = 0;
+        private int mOffset = 0;
+        private byte[] mSingleByte;
+
+        @Override
+        public int available() throws IOException {
+            if (mOffset >= mSharedMemory.getSize()) {
+                return 0;
+            }
+            return mSharedMemory.getSize() - mOffset;
+        }
+
+        @Override
+        public boolean markSupported() {
+            return true;
+        }
+
+        @Override
+        public void mark(int readlimit) {
+            mMark = mOffset;
+        }
+
+        @Override
+        public void reset() throws IOException {
+            mOffset = mMark;
+        }
+
+        @Override
+        public int read() throws IOException {
+            if (mSingleByte == null) {
+                mSingleByte = new byte[1];
+            }
+            int result = read(mSingleByte, 0, 1);
+            if (result != 1) {
+                return -1;
+            }
+            return mSingleByte[0];
+        }
+
+        @Override
+        public int read(byte buffer[], int offset, int count) throws IOException {
+            if (offset < 0 || count < 0 || offset + count > buffer.length) {
+                // readBytes() also does this check, but we need to do it before
+                // changing count.
+                throw new IndexOutOfBoundsException();
+            }
+            count = Math.min(count, available());
+            if (count < 1) {
+                return -1;
+            }
+            int result = readBytes(buffer, mOffset, offset, count);
+            if (result > 0) {
+                mOffset += result;
+            }
+            return result;
+        }
+
+        @Override
+        public long skip(long n) throws IOException {
+            if (mOffset + n > mSharedMemory.getSize()) {
+                n = mSharedMemory.getSize() - mOffset;
+            }
+            mOffset += n;
+            return n;
+        }
+    }
+
+    private class MemoryOutputStream extends OutputStream {
+
+        private int mOffset = 0;
+        private byte[] mSingleByte;
+
+        @Override
+        public void write(byte buffer[], int offset, int count) throws IOException {
+            writeBytes(buffer, offset, mOffset, count);
+            mOffset += count;
+        }
+
+        @Override
+        public void write(int oneByte) throws IOException {
+            if (mSingleByte == null) {
+                mSingleByte = new byte[1];
+            }
+            mSingleByte[0] = (byte)oneByte;
+            write(mSingleByte, 0, 1);
+        }
+    }
+}
diff --git a/android-34/android/os/Message.java b/android-34/android/os/Message.java
new file mode 100644
index 0000000..72fb4ae
--- /dev/null
+++ b/android-34/android/os/Message.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ *
+ * Defines a message containing a description and arbitrary data object that can be
+ * sent to a {@link Handler}.  This object contains two extra int fields and an
+ * extra object field that allow you to not do allocations in many cases.
+ *
+ * <p class="note">While the constructor of Message is public, the best way to get
+ * one of these is to call {@link #obtain Message.obtain()} or one of the
+ * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
+ * them from a pool of recycled objects.</p>
+ */
+public final class Message implements Parcelable {
+    /**
+     * User-defined message code so that the recipient can identify
+     * what this message is about. Each {@link Handler} has its own name-space
+     * for message codes, so you do not need to worry about yours conflicting
+     * with other handlers.
+     */
+    public int what;
+
+    /**
+     * arg1 and arg2 are lower-cost alternatives to using
+     * {@link #setData(Bundle) setData()} if you only need to store a
+     * few integer values.
+     */
+    public int arg1;
+
+    /**
+     * arg1 and arg2 are lower-cost alternatives to using
+     * {@link #setData(Bundle) setData()} if you only need to store a
+     * few integer values.
+     */
+    public int arg2;
+
+    /**
+     * An arbitrary object to send to the recipient.  When using
+     * {@link Messenger} to send the message across processes this can only
+     * be non-null if it contains a Parcelable of a framework class (not one
+     * implemented by the application).   For other data transfer use
+     * {@link #setData}.
+     *
+     * <p>Note that Parcelable objects here are not supported prior to
+     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
+     */
+    public Object obj;
+
+    /**
+     * Optional Messenger where replies to this message can be sent.  The
+     * semantics of exactly how this is used are up to the sender and
+     * receiver.
+     */
+    public Messenger replyTo;
+
+    /**
+     * Indicates that the uid is not set;
+     *
+     * @hide Only for use within the system server.
+     */
+    public static final int UID_NONE = -1;
+
+    /**
+     * Optional field indicating the uid that sent the message.  This is
+     * only valid for messages posted by a {@link Messenger}; otherwise,
+     * it will be -1.
+     */
+    public int sendingUid = UID_NONE;
+
+    /**
+     * Optional field indicating the uid that caused this message to be enqueued.
+     *
+     * @hide Only for use within the system server.
+     */
+    public int workSourceUid = UID_NONE;
+
+    /** If set message is in use.
+     * This flag is set when the message is enqueued and remains set while it
+     * is delivered and afterwards when it is recycled.  The flag is only cleared
+     * when a new message is created or obtained since that is the only time that
+     * applications are allowed to modify the contents of the message.
+     *
+     * It is an error to attempt to enqueue or recycle a message that is already in use.
+     */
+    /*package*/ static final int FLAG_IN_USE = 1 << 0;
+
+    /** If set message is asynchronous */
+    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
+
+    /** Flags to clear in the copyFrom method */
+    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
+
+    @UnsupportedAppUsage
+    /*package*/ int flags;
+
+    /**
+     * The targeted delivery time of this message. The time-base is
+     * {@link SystemClock#uptimeMillis}.
+     * @hide Only for use within the tests.
+     */
+    @UnsupportedAppUsage
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public long when;
+
+    /*package*/ Bundle data;
+
+    @UnsupportedAppUsage
+    /*package*/ Handler target;
+
+    @UnsupportedAppUsage
+    /*package*/ Runnable callback;
+
+    // sometimes we store linked lists of these things
+    @UnsupportedAppUsage
+    /*package*/ Message next;
+
+
+    /** @hide */
+    public static final Object sPoolSync = new Object();
+    private static Message sPool;
+    private static int sPoolSize = 0;
+
+    private static final int MAX_POOL_SIZE = 50;
+
+    private static boolean gCheckRecycle = true;
+
+    /**
+     * Return a new Message instance from the global pool. Allows us to
+     * avoid allocating new objects in many cases.
+     */
+    public static Message obtain() {
+        synchronized (sPoolSync) {
+            if (sPool != null) {
+                Message m = sPool;
+                sPool = m.next;
+                m.next = null;
+                m.flags = 0; // clear in-use flag
+                sPoolSize--;
+                return m;
+            }
+        }
+        return new Message();
+    }
+
+    /**
+     * Same as {@link #obtain()}, but copies the values of an existing
+     * message (including its target) into the new one.
+     * @param orig Original message to copy.
+     * @return A Message object from the global pool.
+     */
+    public static Message obtain(Message orig) {
+        Message m = obtain();
+        m.what = orig.what;
+        m.arg1 = orig.arg1;
+        m.arg2 = orig.arg2;
+        m.obj = orig.obj;
+        m.replyTo = orig.replyTo;
+        m.sendingUid = orig.sendingUid;
+        m.workSourceUid = orig.workSourceUid;
+        if (orig.data != null) {
+            m.data = new Bundle(orig.data);
+        }
+        m.target = orig.target;
+        m.callback = orig.callback;
+
+        return m;
+    }
+
+    /**
+     * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
+     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
+     * @return A Message object from the global pool.
+     */
+    public static Message obtain(Handler h) {
+        Message m = obtain();
+        m.target = h;
+
+        return m;
+    }
+
+    /**
+     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
+     * the Message that is returned.
+     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
+     * @param callback Runnable that will execute when the message is handled.
+     * @return A Message object from the global pool.
+     */
+    public static Message obtain(Handler h, Runnable callback) {
+        Message m = obtain();
+        m.target = h;
+        m.callback = callback;
+
+        return m;
+    }
+
+    /**
+     * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
+     * <em>what</em> members on the Message.
+     * @param h  Value to assign to the <em>target</em> member.
+     * @param what  Value to assign to the <em>what</em> member.
+     * @return A Message object from the global pool.
+     */
+    public static Message obtain(Handler h, int what) {
+        Message m = obtain();
+        m.target = h;
+        m.what = what;
+
+        return m;
+    }
+
+    /**
+     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
+     * members.
+     * @param h  The <em>target</em> value to set.
+     * @param what  The <em>what</em> value to set.
+     * @param obj  The <em>object</em> method to set.
+     * @return  A Message object from the global pool.
+     */
+    public static Message obtain(Handler h, int what, Object obj) {
+        Message m = obtain();
+        m.target = h;
+        m.what = what;
+        m.obj = obj;
+
+        return m;
+    }
+
+    /**
+     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
+     * <em>arg1</em>, and <em>arg2</em> members.
+     *
+     * @param h  The <em>target</em> value to set.
+     * @param what  The <em>what</em> value to set.
+     * @param arg1  The <em>arg1</em> value to set.
+     * @param arg2  The <em>arg2</em> value to set.
+     * @return  A Message object from the global pool.
+     */
+    public static Message obtain(Handler h, int what, int arg1, int arg2) {
+        Message m = obtain();
+        m.target = h;
+        m.what = what;
+        m.arg1 = arg1;
+        m.arg2 = arg2;
+
+        return m;
+    }
+
+    /**
+     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
+     * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
+     *
+     * @param h  The <em>target</em> value to set.
+     * @param what  The <em>what</em> value to set.
+     * @param arg1  The <em>arg1</em> value to set.
+     * @param arg2  The <em>arg2</em> value to set.
+     * @param obj  The <em>obj</em> value to set.
+     * @return  A Message object from the global pool.
+     */
+    public static Message obtain(Handler h, int what,
+            int arg1, int arg2, Object obj) {
+        Message m = obtain();
+        m.target = h;
+        m.what = what;
+        m.arg1 = arg1;
+        m.arg2 = arg2;
+        m.obj = obj;
+
+        return m;
+    }
+
+    /** @hide */
+    public static void updateCheckRecycle(int targetSdkVersion) {
+        if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
+            gCheckRecycle = false;
+        }
+    }
+
+    /**
+     * Return a Message instance to the global pool.
+     * <p>
+     * You MUST NOT touch the Message after calling this function because it has
+     * effectively been freed.  It is an error to recycle a message that is currently
+     * enqueued or that is in the process of being delivered to a Handler.
+     * </p>
+     */
+    public void recycle() {
+        if (isInUse()) {
+            if (gCheckRecycle) {
+                throw new IllegalStateException("This message cannot be recycled because it "
+                        + "is still in use.");
+            }
+            return;
+        }
+        recycleUnchecked();
+    }
+
+    /**
+     * Recycles a Message that may be in-use.
+     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
+     */
+    @UnsupportedAppUsage
+    void recycleUnchecked() {
+        // Mark the message as in use while it remains in the recycled object pool.
+        // Clear out all other details.
+        flags = FLAG_IN_USE;
+        what = 0;
+        arg1 = 0;
+        arg2 = 0;
+        obj = null;
+        replyTo = null;
+        sendingUid = UID_NONE;
+        workSourceUid = UID_NONE;
+        when = 0;
+        target = null;
+        callback = null;
+        data = null;
+
+        synchronized (sPoolSync) {
+            if (sPoolSize < MAX_POOL_SIZE) {
+                next = sPool;
+                sPool = this;
+                sPoolSize++;
+            }
+        }
+    }
+
+    /**
+     * Make this message like o.  Performs a shallow copy of the data field.
+     * Does not copy the linked list fields, nor the timestamp or
+     * target/callback of the original message.
+     */
+    public void copyFrom(Message o) {
+        this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
+        this.what = o.what;
+        this.arg1 = o.arg1;
+        this.arg2 = o.arg2;
+        this.obj = o.obj;
+        this.replyTo = o.replyTo;
+        this.sendingUid = o.sendingUid;
+        this.workSourceUid = o.workSourceUid;
+
+        if (o.data != null) {
+            this.data = (Bundle) o.data.clone();
+        } else {
+            this.data = null;
+        }
+    }
+
+    /**
+     * Return the targeted delivery time of this message, in milliseconds.
+     */
+    public long getWhen() {
+        return when;
+    }
+
+    public void setTarget(Handler target) {
+        this.target = target;
+    }
+
+    /**
+     * Retrieve the {@link android.os.Handler Handler} implementation that
+     * will receive this message. The object must implement
+     * {@link android.os.Handler#handleMessage(android.os.Message)
+     * Handler.handleMessage()}. Each Handler has its own name-space for
+     * message codes, so you do not need to
+     * worry about yours conflicting with other handlers.
+     */
+    public Handler getTarget() {
+        return target;
+    }
+
+    /**
+     * Retrieve callback object that will execute when this message is handled.
+     * This object must implement Runnable. This is called by
+     * the <em>target</em> {@link Handler} that is receiving this Message to
+     * dispatch it.  If
+     * not set, the message will be dispatched to the receiving Handler's
+     * {@link Handler#handleMessage(Message)}.
+     */
+    public Runnable getCallback() {
+        return callback;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public Message setCallback(Runnable r) {
+        callback = r;
+        return this;
+    }
+
+    /**
+     * Obtains a Bundle of arbitrary data associated with this
+     * event, lazily creating it if necessary. Set this value by calling
+     * {@link #setData(Bundle)}.  Note that when transferring data across
+     * processes via {@link Messenger}, you will need to set your ClassLoader
+     * on the Bundle via {@link Bundle#setClassLoader(ClassLoader)
+     * Bundle.setClassLoader()} so that it can instantiate your objects when
+     * you retrieve them.
+     * @see #peekData()
+     * @see #setData(Bundle)
+     */
+    public Bundle getData() {
+        if (data == null) {
+            data = new Bundle();
+        }
+
+        return data;
+    }
+
+    /**
+     * Like getData(), but does not lazily create the Bundle.  A null
+     * is returned if the Bundle does not already exist.  See
+     * {@link #getData} for further information on this.
+     * @see #getData()
+     * @see #setData(Bundle)
+     */
+    public Bundle peekData() {
+        return data;
+    }
+
+    /**
+     * Sets a Bundle of arbitrary data values. Use arg1 and arg2 members
+     * as a lower cost way to send a few simple integer values, if you can.
+     * @see #getData()
+     * @see #peekData()
+     */
+    public void setData(Bundle data) {
+        this.data = data;
+    }
+
+    /**
+     * Chainable setter for {@link #what}
+     *
+     * @hide
+     */
+    public Message setWhat(int what) {
+        this.what = what;
+        return this;
+    }
+
+    /**
+     * Sends this Message to the Handler specified by {@link #getTarget}.
+     * Throws a null pointer exception if this field has not been set.
+     */
+    public void sendToTarget() {
+        target.sendMessage(this);
+    }
+
+    /**
+     * Returns true if the message is asynchronous, meaning that it is not
+     * subject to {@link Looper} synchronization barriers.
+     *
+     * @return True if the message is asynchronous.
+     *
+     * @see #setAsynchronous(boolean)
+     */
+    public boolean isAsynchronous() {
+        return (flags & FLAG_ASYNCHRONOUS) != 0;
+    }
+
+    /**
+     * Sets whether the message is asynchronous, meaning that it is not
+     * subject to {@link Looper} synchronization barriers.
+     * <p>
+     * Certain operations, such as view invalidation, may introduce synchronization
+     * barriers into the {@link Looper}'s message queue to prevent subsequent messages
+     * from being delivered until some condition is met.  In the case of view invalidation,
+     * messages which are posted after a call to {@link android.view.View#invalidate}
+     * are suspended by means of a synchronization barrier until the next frame is
+     * ready to be drawn.  The synchronization barrier ensures that the invalidation
+     * request is completely handled before resuming.
+     * </p><p>
+     * Asynchronous messages are exempt from synchronization barriers.  They typically
+     * represent interrupts, input events, and other signals that must be handled independently
+     * even while other work has been suspended.
+     * </p><p>
+     * Note that asynchronous messages may be delivered out of order with respect to
+     * synchronous messages although they are always delivered in order among themselves.
+     * If the relative order of these messages matters then they probably should not be
+     * asynchronous in the first place.  Use with caution.
+     * </p>
+     *
+     * @param async True if the message is asynchronous.
+     *
+     * @see #isAsynchronous()
+     */
+    public void setAsynchronous(boolean async) {
+        if (async) {
+            flags |= FLAG_ASYNCHRONOUS;
+        } else {
+            flags &= ~FLAG_ASYNCHRONOUS;
+        }
+    }
+
+    /*package*/ boolean isInUse() {
+        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
+    }
+
+    @UnsupportedAppUsage
+    /*package*/ void markInUse() {
+        flags |= FLAG_IN_USE;
+    }
+
+    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
+    */
+    public Message() {
+    }
+
+    @Override
+    public String toString() {
+        return toString(SystemClock.uptimeMillis());
+    }
+
+    @UnsupportedAppUsage
+    String toString(long now) {
+        StringBuilder b = new StringBuilder();
+        b.append("{ when=");
+        TimeUtils.formatDuration(when - now, b);
+
+        if (target != null) {
+            if (callback != null) {
+                b.append(" callback=");
+                b.append(callback.getClass().getName());
+            } else {
+                b.append(" what=");
+                b.append(what);
+            }
+
+            if (arg1 != 0) {
+                b.append(" arg1=");
+                b.append(arg1);
+            }
+
+            if (arg2 != 0) {
+                b.append(" arg2=");
+                b.append(arg2);
+            }
+
+            if (obj != null) {
+                b.append(" obj=");
+                b.append(obj);
+            }
+
+            b.append(" target=");
+            b.append(target.getClass().getName());
+        } else {
+            b.append(" barrier=");
+            b.append(arg1);
+        }
+
+        b.append(" }");
+        return b.toString();
+    }
+
+    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long messageToken = proto.start(fieldId);
+        proto.write(MessageProto.WHEN, when);
+
+        if (target != null) {
+            if (callback != null) {
+                proto.write(MessageProto.CALLBACK, callback.getClass().getName());
+            } else {
+                proto.write(MessageProto.WHAT, what);
+            }
+
+            if (arg1 != 0) {
+                proto.write(MessageProto.ARG1, arg1);
+            }
+
+            if (arg2 != 0) {
+                proto.write(MessageProto.ARG2, arg2);
+            }
+
+            if (obj != null) {
+                proto.write(MessageProto.OBJ, obj.toString());
+            }
+
+            proto.write(MessageProto.TARGET, target.getClass().getName());
+        } else {
+            proto.write(MessageProto.BARRIER, arg1);
+        }
+
+        proto.end(messageToken);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Message> CREATOR
+            = new Parcelable.Creator<Message>() {
+        public Message createFromParcel(Parcel source) {
+            Message msg = Message.obtain();
+            msg.readFromParcel(source);
+            return msg;
+        }
+
+        public Message[] newArray(int size) {
+            return new Message[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        if (callback != null) {
+            throw new RuntimeException(
+                "Can't marshal callbacks across processes.");
+        }
+        dest.writeInt(what);
+        dest.writeInt(arg1);
+        dest.writeInt(arg2);
+        if (obj != null) {
+            try {
+                Parcelable p = (Parcelable)obj;
+                dest.writeInt(1);
+                dest.writeParcelable(p, flags);
+            } catch (ClassCastException e) {
+                throw new RuntimeException(
+                    "Can't marshal non-Parcelable objects across processes.");
+            }
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeLong(when);
+        dest.writeBundle(data);
+        Messenger.writeMessengerOrNullToParcel(replyTo, dest);
+        dest.writeInt(sendingUid);
+        dest.writeInt(workSourceUid);
+    }
+
+    private void readFromParcel(Parcel source) {
+        what = source.readInt();
+        arg1 = source.readInt();
+        arg2 = source.readInt();
+        if (source.readInt() != 0) {
+            obj = source.readParcelable(getClass().getClassLoader(), java.lang.Object.class);
+        }
+        when = source.readLong();
+        data = source.readBundle();
+        replyTo = Messenger.readMessengerOrNullFromParcel(source);
+        sendingUid = source.readInt();
+        workSourceUid = source.readInt();
+    }
+}
diff --git a/android-34/android/os/MessageQueue.java b/android-34/android/os/MessageQueue.java
new file mode 100644
index 0000000..87c4f33
--- /dev/null
+++ b/android-34/android/os/MessageQueue.java
@@ -0,0 +1,1044 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
+import android.util.Printer;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * Low-level class holding the list of messages to be dispatched by a
+ * {@link Looper}.  Messages are not added directly to a MessageQueue,
+ * but rather through {@link Handler} objects associated with the Looper.
+ *
+ * <p>You can retrieve the MessageQueue for the current thread with
+ * {@link Looper#myQueue() Looper.myQueue()}.
+ */
+public final class MessageQueue {
+    private static final String TAG = "MessageQueue";
+    private static final boolean DEBUG = false;
+
+    // True if the message queue can be quit.
+    @UnsupportedAppUsage
+    private final boolean mQuitAllowed;
+
+    @UnsupportedAppUsage
+    @SuppressWarnings("unused")
+    private long mPtr; // used by native code
+
+    @UnsupportedAppUsage
+    Message mMessages;
+    @UnsupportedAppUsage
+    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
+    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
+    private IdleHandler[] mPendingIdleHandlers;
+    private boolean mQuitting;
+
+    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
+    private boolean mBlocked;
+
+    // The next barrier token.
+    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
+    @UnsupportedAppUsage
+    private int mNextBarrierToken;
+
+    private native static long nativeInit();
+    private native static void nativeDestroy(long ptr);
+    @UnsupportedAppUsage
+    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+    private native static void nativeWake(long ptr);
+    private native static boolean nativeIsPolling(long ptr);
+    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
+
+    MessageQueue(boolean quitAllowed) {
+        mQuitAllowed = quitAllowed;
+        mPtr = nativeInit();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            dispose();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    // Disposes of the underlying message queue.
+    // Must only be called on the looper thread or the finalizer.
+    private void dispose() {
+        if (mPtr != 0) {
+            nativeDestroy(mPtr);
+            mPtr = 0;
+        }
+    }
+
+    /**
+     * Returns true if the looper has no pending messages which are due to be processed.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @return True if the looper is idle.
+     */
+    public boolean isIdle() {
+        synchronized (this) {
+            final long now = SystemClock.uptimeMillis();
+            return mMessages == null || now < mMessages.when;
+        }
+    }
+
+    /**
+     * Add a new {@link IdleHandler} to this message queue.  This may be
+     * removed automatically for you by returning false from
+     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
+     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @param handler The IdleHandler to be added.
+     */
+    public void addIdleHandler(@NonNull IdleHandler handler) {
+        if (handler == null) {
+            throw new NullPointerException("Can't add a null IdleHandler");
+        }
+        synchronized (this) {
+            mIdleHandlers.add(handler);
+        }
+    }
+
+    /**
+     * Remove an {@link IdleHandler} from the queue that was previously added
+     * with {@link #addIdleHandler}.  If the given object is not currently
+     * in the idle list, nothing is done.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @param handler The IdleHandler to be removed.
+     */
+    public void removeIdleHandler(@NonNull IdleHandler handler) {
+        synchronized (this) {
+            mIdleHandlers.remove(handler);
+        }
+    }
+
+    /**
+     * Returns whether this looper's thread is currently polling for more work to do.
+     * This is a good signal that the loop is still alive rather than being stuck
+     * handling a callback.  Note that this method is intrinsically racy, since the
+     * state of the loop can change before you get the result back.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @return True if the looper is currently polling for events.
+     * @hide
+     */
+    public boolean isPolling() {
+        synchronized (this) {
+            return isPollingLocked();
+        }
+    }
+
+    private boolean isPollingLocked() {
+        // If the loop is quitting then it must not be idling.
+        // We can assume mPtr != 0 when mQuitting is false.
+        return !mQuitting && nativeIsPolling(mPtr);
+    }
+
+    /**
+     * Adds a file descriptor listener to receive notification when file descriptor
+     * related events occur.
+     * <p>
+     * If the file descriptor has already been registered, the specified events
+     * and listener will replace any that were previously associated with it.
+     * It is not possible to set more than one listener per file descriptor.
+     * </p><p>
+     * It is important to always unregister the listener when the file descriptor
+     * is no longer of use.
+     * </p>
+     *
+     * @param fd The file descriptor for which a listener will be registered.
+     * @param events The set of events to receive: a combination of the
+     * {@link OnFileDescriptorEventListener#EVENT_INPUT},
+     * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and
+     * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks.  If the requested
+     * set of events is zero, then the listener is unregistered.
+     * @param listener The listener to invoke when file descriptor events occur.
+     *
+     * @see OnFileDescriptorEventListener
+     * @see #removeOnFileDescriptorEventListener
+     */
+    public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
+            @OnFileDescriptorEventListener.Events int events,
+            @NonNull OnFileDescriptorEventListener listener) {
+        if (fd == null) {
+            throw new IllegalArgumentException("fd must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+
+        synchronized (this) {
+            updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+        }
+    }
+
+    /**
+     * Removes a file descriptor listener.
+     * <p>
+     * This method does nothing if no listener has been registered for the
+     * specified file descriptor.
+     * </p>
+     *
+     * @param fd The file descriptor whose listener will be unregistered.
+     *
+     * @see OnFileDescriptorEventListener
+     * @see #addOnFileDescriptorEventListener
+     */
+    public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
+        if (fd == null) {
+            throw new IllegalArgumentException("fd must not be null");
+        }
+
+        synchronized (this) {
+            updateOnFileDescriptorEventListenerLocked(fd, 0, null);
+        }
+    }
+
+    private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
+            OnFileDescriptorEventListener listener) {
+        final int fdNum = fd.getInt$();
+
+        int index = -1;
+        FileDescriptorRecord record = null;
+        if (mFileDescriptorRecords != null) {
+            index = mFileDescriptorRecords.indexOfKey(fdNum);
+            if (index >= 0) {
+                record = mFileDescriptorRecords.valueAt(index);
+                if (record != null && record.mEvents == events) {
+                    return;
+                }
+            }
+        }
+
+        if (events != 0) {
+            events |= OnFileDescriptorEventListener.EVENT_ERROR;
+            if (record == null) {
+                if (mFileDescriptorRecords == null) {
+                    mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
+                }
+                record = new FileDescriptorRecord(fd, events, listener);
+                mFileDescriptorRecords.put(fdNum, record);
+            } else {
+                record.mListener = listener;
+                record.mEvents = events;
+                record.mSeq += 1;
+            }
+            nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+        } else if (record != null) {
+            record.mEvents = 0;
+            mFileDescriptorRecords.removeAt(index);
+            nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
+        }
+    }
+
+    // Called from native code.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private int dispatchEvents(int fd, int events) {
+        // Get the file descriptor record and any state that might change.
+        final FileDescriptorRecord record;
+        final int oldWatchedEvents;
+        final OnFileDescriptorEventListener listener;
+        final int seq;
+        synchronized (this) {
+            record = mFileDescriptorRecords.get(fd);
+            if (record == null) {
+                return 0; // spurious, no listener registered
+            }
+
+            oldWatchedEvents = record.mEvents;
+            events &= oldWatchedEvents; // filter events based on current watched set
+            if (events == 0) {
+                return oldWatchedEvents; // spurious, watched events changed
+            }
+
+            listener = record.mListener;
+            seq = record.mSeq;
+        }
+
+        // Invoke the listener outside of the lock.
+        int newWatchedEvents = listener.onFileDescriptorEvents(
+                record.mDescriptor, events);
+        if (newWatchedEvents != 0) {
+            newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
+        }
+
+        // Update the file descriptor record if the listener changed the set of
+        // events to watch and the listener itself hasn't been updated since.
+        if (newWatchedEvents != oldWatchedEvents) {
+            synchronized (this) {
+                int index = mFileDescriptorRecords.indexOfKey(fd);
+                if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
+                        && record.mSeq == seq) {
+                    record.mEvents = newWatchedEvents;
+                    if (newWatchedEvents == 0) {
+                        mFileDescriptorRecords.removeAt(index);
+                    }
+                }
+            }
+        }
+
+        // Return the new set of events to watch for native code to take care of.
+        return newWatchedEvents;
+    }
+
+    @UnsupportedAppUsage
+    Message next() {
+        // Return here if the message loop has already quit and been disposed.
+        // This can happen if the application tries to restart a looper after quit
+        // which is not supported.
+        final long ptr = mPtr;
+        if (ptr == 0) {
+            return null;
+        }
+
+        int pendingIdleHandlerCount = -1; // -1 only during first iteration
+        int nextPollTimeoutMillis = 0;
+        for (;;) {
+            if (nextPollTimeoutMillis != 0) {
+                Binder.flushPendingCommands();
+            }
+
+            nativePollOnce(ptr, nextPollTimeoutMillis);
+
+            synchronized (this) {
+                // Try to retrieve the next message.  Return if found.
+                final long now = SystemClock.uptimeMillis();
+                Message prevMsg = null;
+                Message msg = mMessages;
+                if (msg != null && msg.target == null) {
+                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
+                    do {
+                        prevMsg = msg;
+                        msg = msg.next;
+                    } while (msg != null && !msg.isAsynchronous());
+                }
+                if (msg != null) {
+                    if (now < msg.when) {
+                        // Next message is not ready.  Set a timeout to wake up when it is ready.
+                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
+                    } else {
+                        // Got a message.
+                        mBlocked = false;
+                        if (prevMsg != null) {
+                            prevMsg.next = msg.next;
+                        } else {
+                            mMessages = msg.next;
+                        }
+                        msg.next = null;
+                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
+                        msg.markInUse();
+                        return msg;
+                    }
+                } else {
+                    // No more messages.
+                    nextPollTimeoutMillis = -1;
+                }
+
+                // Process the quit message now that all pending messages have been handled.
+                if (mQuitting) {
+                    dispose();
+                    return null;
+                }
+
+                // If first time idle, then get the number of idlers to run.
+                // Idle handles only run if the queue is empty or if the first message
+                // in the queue (possibly a barrier) is due to be handled in the future.
+                if (pendingIdleHandlerCount < 0
+                        && (mMessages == null || now < mMessages.when)) {
+                    pendingIdleHandlerCount = mIdleHandlers.size();
+                }
+                if (pendingIdleHandlerCount <= 0) {
+                    // No idle handlers to run.  Loop and wait some more.
+                    mBlocked = true;
+                    continue;
+                }
+
+                if (mPendingIdleHandlers == null) {
+                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
+                }
+                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
+            }
+
+            // Run the idle handlers.
+            // We only ever reach this code block during the first iteration.
+            for (int i = 0; i < pendingIdleHandlerCount; i++) {
+                final IdleHandler idler = mPendingIdleHandlers[i];
+                mPendingIdleHandlers[i] = null; // release the reference to the handler
+
+                boolean keep = false;
+                try {
+                    keep = idler.queueIdle();
+                } catch (Throwable t) {
+                    Log.wtf(TAG, "IdleHandler threw exception", t);
+                }
+
+                if (!keep) {
+                    synchronized (this) {
+                        mIdleHandlers.remove(idler);
+                    }
+                }
+            }
+
+            // Reset the idle handler count to 0 so we do not run them again.
+            pendingIdleHandlerCount = 0;
+
+            // While calling an idle handler, a new message could have been delivered
+            // so go back and look again for a pending message without waiting.
+            nextPollTimeoutMillis = 0;
+        }
+    }
+
+    void quit(boolean safe) {
+        if (!mQuitAllowed) {
+            throw new IllegalStateException("Main thread not allowed to quit.");
+        }
+
+        synchronized (this) {
+            if (mQuitting) {
+                return;
+            }
+            mQuitting = true;
+
+            if (safe) {
+                removeAllFutureMessagesLocked();
+            } else {
+                removeAllMessagesLocked();
+            }
+
+            // We can assume mPtr != 0 because mQuitting was previously false.
+            nativeWake(mPtr);
+        }
+    }
+
+    /**
+     * Posts a synchronization barrier to the Looper's message queue.
+     *
+     * Message processing occurs as usual until the message queue encounters the
+     * synchronization barrier that has been posted.  When the barrier is encountered,
+     * later synchronous messages in the queue are stalled (prevented from being executed)
+     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
+     * the token that identifies the synchronization barrier.
+     *
+     * This method is used to immediately postpone execution of all subsequently posted
+     * synchronous messages until a condition is met that releases the barrier.
+     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
+     * and continue to be processed as usual.
+     *
+     * This call must be always matched by a call to {@link #removeSyncBarrier} with
+     * the same token to ensure that the message queue resumes normal operation.
+     * Otherwise the application will probably hang!
+     *
+     * @return A token that uniquely identifies the barrier.  This token must be
+     * passed to {@link #removeSyncBarrier} to release the barrier.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public int postSyncBarrier() {
+        return postSyncBarrier(SystemClock.uptimeMillis());
+    }
+
+    private int postSyncBarrier(long when) {
+        // Enqueue a new sync barrier token.
+        // We don't need to wake the queue because the purpose of a barrier is to stall it.
+        synchronized (this) {
+            final int token = mNextBarrierToken++;
+            final Message msg = Message.obtain();
+            msg.markInUse();
+            msg.when = when;
+            msg.arg1 = token;
+
+            Message prev = null;
+            Message p = mMessages;
+            if (when != 0) {
+                while (p != null && p.when <= when) {
+                    prev = p;
+                    p = p.next;
+                }
+            }
+            if (prev != null) { // invariant: p == prev.next
+                msg.next = p;
+                prev.next = msg;
+            } else {
+                msg.next = p;
+                mMessages = msg;
+            }
+            return token;
+        }
+    }
+
+    /**
+     * Removes a synchronization barrier.
+     *
+     * @param token The synchronization barrier token that was returned by
+     * {@link #postSyncBarrier}.
+     *
+     * @throws IllegalStateException if the barrier was not found.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public void removeSyncBarrier(int token) {
+        // Remove a sync barrier token from the queue.
+        // If the queue is no longer stalled by a barrier then wake it.
+        synchronized (this) {
+            Message prev = null;
+            Message p = mMessages;
+            while (p != null && (p.target != null || p.arg1 != token)) {
+                prev = p;
+                p = p.next;
+            }
+            if (p == null) {
+                throw new IllegalStateException("The specified message queue synchronization "
+                        + " barrier token has not been posted or has already been removed.");
+            }
+            final boolean needWake;
+            if (prev != null) {
+                prev.next = p.next;
+                needWake = false;
+            } else {
+                mMessages = p.next;
+                needWake = mMessages == null || mMessages.target != null;
+            }
+            p.recycleUnchecked();
+
+            // If the loop is quitting then it is already awake.
+            // We can assume mPtr != 0 when mQuitting is false.
+            if (needWake && !mQuitting) {
+                nativeWake(mPtr);
+            }
+        }
+    }
+
+    boolean enqueueMessage(Message msg, long when) {
+        if (msg.target == null) {
+            throw new IllegalArgumentException("Message must have a target.");
+        }
+
+        synchronized (this) {
+            if (msg.isInUse()) {
+                throw new IllegalStateException(msg + " This message is already in use.");
+            }
+
+            if (mQuitting) {
+                IllegalStateException e = new IllegalStateException(
+                        msg.target + " sending message to a Handler on a dead thread");
+                Log.w(TAG, e.getMessage(), e);
+                msg.recycle();
+                return false;
+            }
+
+            msg.markInUse();
+            msg.when = when;
+            Message p = mMessages;
+            boolean needWake;
+            if (p == null || when == 0 || when < p.when) {
+                // New head, wake up the event queue if blocked.
+                msg.next = p;
+                mMessages = msg;
+                needWake = mBlocked;
+            } else {
+                // Inserted within the middle of the queue.  Usually we don't have to wake
+                // up the event queue unless there is a barrier at the head of the queue
+                // and the message is the earliest asynchronous message in the queue.
+                needWake = mBlocked && p.target == null && msg.isAsynchronous();
+                Message prev;
+                for (;;) {
+                    prev = p;
+                    p = p.next;
+                    if (p == null || when < p.when) {
+                        break;
+                    }
+                    if (needWake && p.isAsynchronous()) {
+                        needWake = false;
+                    }
+                }
+                msg.next = p; // invariant: p == prev.next
+                prev.next = msg;
+            }
+
+            // We can assume mPtr != 0 because mQuitting is false.
+            if (needWake) {
+                nativeWake(mPtr);
+            }
+        }
+        return true;
+    }
+
+    boolean hasMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    boolean hasEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h && p.what == what && (object == null || object.equals(p.obj))) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    boolean hasMessages(Handler h, Runnable r, Object object) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    boolean hasMessages(Handler h) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    void removeMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.what == what
+                   && (object == null || p.obj == object)) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.what == what
+                        && (object == null || n.obj == object)) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    void removeEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.what == what
+                   && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.what == what
+                        && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    void removeMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.callback == r
+                   && (object == null || p.obj == object)) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.callback == r
+                        && (object == null || n.obj == object)) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    void removeEqualMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.callback == r
+                   && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.callback == r
+                        && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+
+    void removeCallbacksAndMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h
+                    && (object == null || p.obj == object)) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && (object == null || n.obj == object)) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    void removeCallbacksAndEqualMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h
+                    && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    private void removeAllMessagesLocked() {
+        Message p = mMessages;
+        while (p != null) {
+            Message n = p.next;
+            p.recycleUnchecked();
+            p = n;
+        }
+        mMessages = null;
+    }
+
+    private void removeAllFutureMessagesLocked() {
+        final long now = SystemClock.uptimeMillis();
+        Message p = mMessages;
+        if (p != null) {
+            if (p.when > now) {
+                removeAllMessagesLocked();
+            } else {
+                Message n;
+                for (;;) {
+                    n = p.next;
+                    if (n == null) {
+                        return;
+                    }
+                    if (n.when > now) {
+                        break;
+                    }
+                    p = n;
+                }
+                p.next = null;
+                do {
+                    p = n;
+                    n = p.next;
+                    p.recycleUnchecked();
+                } while (n != null);
+            }
+        }
+    }
+
+    void dump(Printer pw, String prefix, Handler h) {
+        synchronized (this) {
+            long now = SystemClock.uptimeMillis();
+            int n = 0;
+            for (Message msg = mMessages; msg != null; msg = msg.next) {
+                if (h == null || h == msg.target) {
+                    pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+                }
+                n++;
+            }
+            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
+                    + ", quitting=" + mQuitting + ")");
+        }
+    }
+
+    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long messageQueueToken = proto.start(fieldId);
+        synchronized (this) {
+            for (Message msg = mMessages; msg != null; msg = msg.next) {
+                msg.dumpDebug(proto, MessageQueueProto.MESSAGES);
+            }
+            proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
+            proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
+        }
+        proto.end(messageQueueToken);
+    }
+
+    /**
+     * Callback interface for discovering when a thread is going to block
+     * waiting for more messages.
+     */
+    public static interface IdleHandler {
+        /**
+         * Called when the message queue has run out of messages and will now
+         * wait for more.  Return true to keep your idle handler active, false
+         * to have it removed.  This may be called if there are still messages
+         * pending in the queue, but they are all scheduled to be dispatched
+         * after the current time.
+         */
+        boolean queueIdle();
+    }
+
+    /**
+     * A listener which is invoked when file descriptor related events occur.
+     */
+    public interface OnFileDescriptorEventListener {
+        /**
+         * File descriptor event: Indicates that the file descriptor is ready for input
+         * operations, such as reading.
+         * <p>
+         * The listener should read all available data from the file descriptor
+         * then return <code>true</code> to keep the listener active or <code>false</code>
+         * to remove the listener.
+         * </p><p>
+         * In the case of a socket, this event may be generated to indicate
+         * that there is at least one incoming connection that the listener
+         * should accept.
+         * </p><p>
+         * This event will only be generated if the {@link #EVENT_INPUT} event mask was
+         * specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_INPUT = 1 << 0;
+
+        /**
+         * File descriptor event: Indicates that the file descriptor is ready for output
+         * operations, such as writing.
+         * <p>
+         * The listener should write as much data as it needs.  If it could not
+         * write everything at once, then it should return <code>true</code> to
+         * keep the listener active.  Otherwise, it should return <code>false</code>
+         * to remove the listener then re-register it later when it needs to write
+         * something else.
+         * </p><p>
+         * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was
+         * specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_OUTPUT = 1 << 1;
+
+        /**
+         * File descriptor event: Indicates that the file descriptor encountered a
+         * fatal error.
+         * <p>
+         * File descriptor errors can occur for various reasons.  One common error
+         * is when the remote peer of a socket or pipe closes its end of the connection.
+         * </p><p>
+         * This event may be generated at any time regardless of whether the
+         * {@link #EVENT_ERROR} event mask was specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_ERROR = 1 << 2;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+                EVENT_INPUT,
+                EVENT_OUTPUT,
+                EVENT_ERROR
+        })
+        public @interface Events {}
+
+        /**
+         * Called when a file descriptor receives events.
+         *
+         * @param fd The file descriptor.
+         * @param events The set of events that occurred: a combination of the
+         * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.
+         * @return The new set of events to watch, or 0 to unregister the listener.
+         *
+         * @see #EVENT_INPUT
+         * @see #EVENT_OUTPUT
+         * @see #EVENT_ERROR
+         */
+        @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
+    }
+
+    private static final class FileDescriptorRecord {
+        public final FileDescriptor mDescriptor;
+        public int mEvents;
+        public OnFileDescriptorEventListener mListener;
+        public int mSeq;
+
+        public FileDescriptorRecord(FileDescriptor descriptor,
+                int events, OnFileDescriptorEventListener listener) {
+            mDescriptor = descriptor;
+            mEvents = events;
+            mListener = listener;
+        }
+    }
+}
diff --git a/android-34/android/os/Messenger.java b/android-34/android/os/Messenger.java
new file mode 100644
index 0000000..ed851b3
--- /dev/null
+++ b/android-34/android/os/Messenger.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.Nullable;
+
+/**
+ * Reference to a Handler, which others can use to send messages to it.
+ * This allows for the implementation of message-based communication across
+ * processes, by creating a Messenger pointing to a Handler in one process,
+ * and handing that Messenger to another process.
+ *
+ * <p>Note: the implementation underneath is just a simple wrapper around
+ * a {@link Binder} that is used to perform the communication.  This means
+ * semantically you should treat it as such: this class does not impact process
+ * lifecycle management (you must be using some higher-level component to tell
+ * the system that your process needs to continue running), the connection will
+ * break if your process goes away for any reason, etc.</p>
+ */
+public final class Messenger implements Parcelable {
+    private final IMessenger mTarget;
+
+    /**
+     * Create a new Messenger pointing to the given Handler.  Any Message
+     * objects sent through this Messenger will appear in the Handler as if
+     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
+     * been called directly.
+     * 
+     * @param target The Handler that will receive sent messages.
+     */
+    public Messenger(Handler target) {
+        mTarget = target.getIMessenger();
+    }
+    
+    /**
+     * Send a Message to this Messenger's Handler.
+     * 
+     * @param message The Message to send.  Usually retrieved through
+     * {@link Message#obtain() Message.obtain()}.
+     * 
+     * @throws RemoteException Throws DeadObjectException if the target
+     * Handler no longer exists.
+     */
+    public void send(Message message) throws RemoteException {
+        mTarget.send(message);
+    }
+    
+    /**
+     * Retrieve the IBinder that this Messenger is using to communicate with
+     * its associated Handler.
+     * 
+     * @return Returns the IBinder backing this Messenger.
+     */
+    public IBinder getBinder() {
+        return mTarget.asBinder();
+    }
+    
+    /**
+     * Comparison operator on two Messenger objects, such that true
+     * is returned then they both point to the same Handler.
+     */
+    public boolean equals(@Nullable Object otherObj) {
+        if (otherObj == null) {
+            return false;
+        }
+        try {
+            return mTarget.asBinder().equals(((Messenger)otherObj)
+                    .mTarget.asBinder());
+        } catch (ClassCastException e) {
+        }
+        return false;
+    }
+
+    public int hashCode() {
+        return mTarget.asBinder().hashCode();
+    }
+    
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStrongBinder(mTarget.asBinder());
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Messenger> CREATOR
+            = new Parcelable.Creator<Messenger>() {
+        public Messenger createFromParcel(Parcel in) {
+            IBinder target = in.readStrongBinder();
+            return target != null ? new Messenger(target) : null;
+        }
+
+        public Messenger[] newArray(int size) {
+            return new Messenger[size];
+        }
+    };
+
+    /**
+     * Convenience function for writing either a Messenger or null pointer to
+     * a Parcel.  You must use this with {@link #readMessengerOrNullFromParcel}
+     * for later reading it.
+     * 
+     * @param messenger The Messenger to write, or null.
+     * @param out Where to write the Messenger.
+     */
+    public static void writeMessengerOrNullToParcel(Messenger messenger,
+            Parcel out) {
+        out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
+                : null);
+    }
+    
+    /**
+     * Convenience function for reading either a Messenger or null pointer from
+     * a Parcel.  You must have previously written the Messenger with
+     * {@link #writeMessengerOrNullToParcel}.
+     * 
+     * @param in The Parcel containing the written Messenger.
+     * 
+     * @return Returns the Messenger read from the Parcel, or null if null had
+     * been written.
+     */
+    public static Messenger readMessengerOrNullFromParcel(Parcel in) {
+        IBinder b = in.readStrongBinder();
+        return b != null ? new Messenger(b) : null;
+    }
+    
+    /**
+     * Create a Messenger from a raw IBinder, which had previously been
+     * retrieved with {@link #getBinder}.
+     * 
+     * @param target The IBinder this Messenger should communicate with.
+     */
+    public Messenger(IBinder target) {
+        mTarget = IMessenger.Stub.asInterface(target);
+    }
+}
diff --git a/android-34/android/os/NativeHandle.java b/android-34/android/os/NativeHandle.java
new file mode 100644
index 0000000..a26873a
--- /dev/null
+++ b/android-34/android/os/NativeHandle.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+
+/**
+ * Collection representing a set of open file descriptors and an opaque data stream.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NativeHandle implements Closeable {
+    // whether this object owns mFds
+    private boolean mOwn = false;
+    private FileDescriptor[] mFds;
+    private int[] mInts;
+
+    /**
+     * Constructs a {@link NativeHandle} object containing
+     * zero file descriptors and an empty data stream.
+     */
+    public NativeHandle() {
+        this(new FileDescriptor[0], new int[0], false);
+    }
+
+    /**
+     * Constructs a {@link NativeHandle} object containing the given
+     * {@link FileDescriptor} object and an empty data stream.
+     */
+    public NativeHandle(@NonNull FileDescriptor descriptor, boolean own) {
+        this(new FileDescriptor[] {descriptor}, new int[0], own);
+    }
+
+    /**
+     * Convenience method for creating a list of file descriptors.
+     *
+     * @hide
+     */
+    private static FileDescriptor[] createFileDescriptorArray(@NonNull int[] fds) {
+        FileDescriptor[] list = new FileDescriptor[fds.length];
+        for (int i = 0; i < fds.length; i++) {
+            FileDescriptor descriptor = new FileDescriptor();
+            descriptor.setInt$(fds[i]);
+            list[i] = descriptor;
+        }
+        return list;
+    }
+
+    /**
+     * Convenience method for instantiating a {@link NativeHandle} from JNI. It does
+     * not take ownership of the int[] params. It does not dupe the FileDescriptors.
+     *
+     * @hide
+     */
+    private NativeHandle(@NonNull int[] fds, @NonNull int[] ints, boolean own) {
+        this(createFileDescriptorArray(fds), ints, own);
+    }
+
+    /**
+     * Instantiate an opaque {@link NativeHandle} from fds and integers.
+     *
+     * @param own whether the fds are owned by this object and should be closed
+     */
+    public NativeHandle(@NonNull FileDescriptor[] fds, @NonNull int[] ints, boolean own) {
+        mFds = fds.clone();
+        mInts = ints.clone();
+        mOwn = own;
+    }
+
+    /**
+     * Returns whether this {@link NativeHandle} object contains a single file
+     * descriptor and nothing else.
+     *
+     * @return a boolean value
+     */
+    public boolean hasSingleFileDescriptor() {
+        checkOpen();
+
+        return mFds.length == 1 && mInts.length == 0;
+    }
+
+    /**
+     * Explicitly duplicate NativeHandle (this dups all file descritptors).
+     *
+     * If this method is called, this must also be explicitly closed with
+     * {@link #close()}.
+     */
+    public @NonNull NativeHandle dup() throws java.io.IOException {
+        FileDescriptor[] fds = new FileDescriptor[mFds.length];
+        try {
+            for (int i = 0; i < mFds.length; i++) {
+                FileDescriptor newFd = new FileDescriptor();
+                int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0);
+                newFd.setInt$(fdint);
+                fds[i] = newFd;
+            }
+        } catch (ErrnoException e) {
+            e.rethrowAsIOException();
+        }
+        return new NativeHandle(fds, mInts, true /*own*/);
+    }
+
+    private void checkOpen() {
+        if (mFds == null) {
+            throw new IllegalStateException("NativeHandle is invalidated after close.");
+        }
+    }
+
+    /**
+     * Closes the file descriptors if they are owned by this object.
+     *
+     * This also invalidates the object.
+     */
+    @Override
+    public void close() throws java.io.IOException {
+        checkOpen();
+
+        if (mOwn) {
+            try {
+                for (FileDescriptor fd : mFds) {
+                    Os.close(fd);
+                }
+            } catch (ErrnoException e) {
+                e.rethrowAsIOException();
+            }
+
+            mOwn = false;
+        }
+
+        mFds = null;
+        mInts = null;
+    }
+
+    /**
+     * Returns the underlying lone file descriptor.
+     *
+     * @return a {@link FileDescriptor} object
+     * @throws IllegalStateException if this object contains either zero or
+     *         more than one file descriptor, or a non-empty data stream.
+     */
+    public @NonNull FileDescriptor getFileDescriptor() {
+        checkOpen();
+
+        if (!hasSingleFileDescriptor()) {
+            throw new IllegalStateException(
+                    "NativeHandle is not single file descriptor. Contents must"
+                    + " be retreived through getFileDescriptors and getInts.");
+        }
+
+        return mFds[0];
+    }
+
+    /**
+     * Convenience method for fetching this object's file descriptors from JNI.
+     * @return a mutable copy of the underlying file descriptors (as an int[])
+     *
+     * @hide
+     */
+    private int[] getFdsAsIntArray() {
+        checkOpen();
+
+        int numFds = mFds.length;
+        int[] fds = new int[numFds];
+
+        for (int i = 0; i < numFds; i++) {
+            fds[i] = mFds[i].getInt$();
+        }
+
+        return fds;
+    }
+
+    /**
+     * Fetch file descriptors
+     *
+     * @return the fds.
+     */
+    public @NonNull FileDescriptor[] getFileDescriptors() {
+        checkOpen();
+
+        return mFds;
+    }
+
+    /**
+     * Fetch opaque ints. Note: This object retains ownership of the data.
+     *
+     * @return the opaque data stream.
+     */
+    public @NonNull int[] getInts() {
+        checkOpen();
+
+        return mInts;
+    }
+}
diff --git a/android-34/android/os/NetworkOnMainThreadException.java b/android-34/android/os/NetworkOnMainThreadException.java
new file mode 100644
index 0000000..dd8c66c
--- /dev/null
+++ b/android-34/android/os/NetworkOnMainThreadException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+/**
+ * The exception that is thrown when an application attempts
+ * to perform a networking operation on its main thread.
+ *
+ * <p>This is only thrown for applications targeting the Honeycomb
+ * SDK or higher.  Applications targeting earlier SDK versions
+ * are allowed to do networking on their main event loop threads,
+ * but it's heavily discouraged.  See the document
+ * <a href="{@docRoot}guide/practices/design/responsiveness.html">
+ * Designing for Responsiveness</a>.
+ *
+ * <p>Also see {@link StrictMode}.
+ */
+public class NetworkOnMainThreadException extends RuntimeException {
+}
diff --git a/android-34/android/os/NewUserRequest.java b/android-34/android/os/NewUserRequest.java
new file mode 100644
index 0000000..45ad74e
--- /dev/null
+++ b/android-34/android/os/NewUserRequest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+
+/**
+ * Contains necessary information to create user using
+ * {@link UserManager#createUser(NewUserRequest)}.
+ *
+ * @hide
+ */
+@SystemApi
+@SuppressLint("PackageLayering")
+public final class NewUserRequest {
+    @Nullable
+    private final String mName;
+    private final boolean mAdmin;
+    private final boolean mEphemeral;
+    @NonNull
+    private final String mUserType;
+    private final Bitmap mUserIcon;
+    private final String mAccountName;
+    private final String mAccountType;
+    private final PersistableBundle mAccountOptions;
+
+    private NewUserRequest(Builder builder) {
+        mName = builder.mName;
+        mAdmin = builder.mAdmin;
+        mEphemeral = builder.mEphemeral;
+        mUserType = builder.mUserType;
+        mUserIcon = builder.mUserIcon;
+        mAccountName = builder.mAccountName;
+        mAccountType = builder.mAccountType;
+        mAccountOptions = builder.mAccountOptions;
+    }
+
+    /**
+     * Returns the name of the user.
+     */
+    @Nullable
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns whether the user is ephemeral.
+     *
+     * <p> Ephemeral user will be removed after leaving the foreground.
+     */
+    public boolean isEphemeral() {
+        return mEphemeral;
+    }
+
+    /**
+     * Returns whether the user is an admin.
+     *
+     * <p> Admin user is with administrative privileges and such user can create and
+     * delete users.
+     */
+    public boolean isAdmin() {
+        return mAdmin;
+    }
+
+    /**
+     * Returns the calculated flags for user creation.
+     */
+    int getFlags() {
+        int flags = 0;
+        if (isAdmin()) flags |= UserInfo.FLAG_ADMIN;
+        if (isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL;
+        return flags;
+    }
+
+    /**
+     * Returns the user type.
+     *
+     * <p> Default value is {@link UserManager#USER_TYPE_FULL_SECONDARY}
+     */
+    @NonNull
+    public String getUserType() {
+        return mUserType;
+    }
+
+    /**
+     * Returns the user icon.
+     */
+    @Nullable
+    public Bitmap getUserIcon() {
+        return mUserIcon;
+    }
+
+    /**
+     * Returns the account name.
+     */
+    @Nullable
+    public String getAccountName() {
+        return mAccountName;
+    }
+
+    /**
+     * Returns the account type.
+     */
+    @Nullable
+    public String getAccountType() {
+        return mAccountType;
+    }
+
+    /**
+     * Returns the account options.
+     */
+    @SuppressLint("NullableCollection")
+    @Nullable
+    public PersistableBundle getAccountOptions() {
+        return mAccountOptions;
+    }
+
+    @Override
+    public String toString() {
+        return "NewUserRequest{"
+                + "mName='" + mName + '\''
+                + ", mAdmin=" + mAdmin
+                + ", mEphemeral=" + mEphemeral
+                + ", mUserType='" + mUserType + '\''
+                + ", mAccountName='" + mAccountName + '\''
+                + ", mAccountType='" + mAccountType + '\''
+                + ", mAccountOptions=" + mAccountOptions
+                + '}';
+    }
+
+    /**
+     * Builder for building {@link NewUserRequest}
+     */
+    @SuppressLint("PackageLayering")
+    public static final class Builder {
+
+        private String mName;
+        private boolean mAdmin;
+        private boolean mEphemeral;
+        private String mUserType = UserManager.USER_TYPE_FULL_SECONDARY;
+        private Bitmap mUserIcon;
+        private String mAccountName;
+        private String mAccountType;
+        private PersistableBundle mAccountOptions;
+
+        /**
+         * Sets user name.
+         *
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setName(@Nullable String name) {
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Sets user as admin.
+         *
+         * <p> Admin user is with administrative privileges and such user can create
+         * and delete users.
+         *
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setAdmin() {
+            mAdmin = true;
+            return this;
+        }
+
+        /**
+         * Sets user as ephemeral.
+         *
+         * <p> Ephemeral user will be removed after leaving the foreground.
+         *
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setEphemeral() {
+            mEphemeral = true;
+            return this;
+        }
+
+        /**
+         * Sets user type.
+         *
+         * <p> Default value is {link UserManager#USER_TYPE_FULL_SECONDARY}.
+         *
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setUserType(@NonNull String type) {
+            mUserType = type;
+            return this;
+        }
+
+        /**
+         * Sets user icon.
+         *
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setUserIcon(@Nullable Bitmap userIcon) {
+            mUserIcon = userIcon;
+            return this;
+        }
+
+        /**
+         * Sets account name that will be used by the setup wizard to initialize the user.
+         *
+         * @see android.accounts.Account
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setAccountName(@Nullable String accountName) {
+            mAccountName = accountName;
+            return this;
+        }
+
+        /**
+         * Sets account type for the account to be created. This is required if the account name
+         * is not null. This will be used by the setup wizard to initialize the user.
+         *
+         * @see android.accounts.Account
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setAccountType(@Nullable String accountType) {
+            mAccountType = accountType;
+            return this;
+        }
+
+        /**
+         * Sets account options that can contain account-specific extra information
+         * to be used by setup wizard to initialize the account for the user.
+         *
+         * @return This object for method chaining.
+         */
+        @NonNull
+        public Builder setAccountOptions(@Nullable PersistableBundle accountOptions) {
+            mAccountOptions = accountOptions;
+            return this;
+        }
+
+        /**
+         * Builds {@link NewUserRequest}
+         *
+         * @throws IllegalStateException if builder is configured with incompatible properties and
+         * it is not possible to create such user. For example - a guest admin user.
+         */
+        @NonNull
+        public NewUserRequest build() {
+            checkIfPropertiesAreCompatible();
+            return new NewUserRequest(this);
+        }
+
+        private void checkIfPropertiesAreCompatible() {
+            if (mUserType == null) {
+                throw new IllegalStateException("Usertype cannot be null");
+            }
+
+            // Admin user can only be USER_TYPE_FULL_SECONDARY
+            if (mAdmin && !mUserType.equals(UserManager.USER_TYPE_FULL_SECONDARY)) {
+                throw new IllegalStateException("Admin user can't be of type: " + mUserType);
+            }
+
+            if (TextUtils.isEmpty(mAccountName) != TextUtils.isEmpty(mAccountType)) {
+                throw new IllegalStateException(
+                        "Account name and account type should be provided together.");
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/NewUserResponse.java b/android-34/android/os/NewUserResponse.java
new file mode 100644
index 0000000..f48d9e5
--- /dev/null
+++ b/android-34/android/os/NewUserResponse.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+
+/**
+ * Contains the response of the call {@link UserManager#createUser(NewUserRequest)}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NewUserResponse {
+
+    private final @Nullable UserHandle mUser;
+    private final @UserManager.UserOperationResult int mOperationResult;
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public NewUserResponse(@Nullable UserHandle user,
+            @UserManager.UserOperationResult int operationResult) {
+        mUser = user;
+        mOperationResult = operationResult;
+    }
+
+    /**
+     * Is user creation successful?
+     */
+    public boolean isSuccessful() {
+        return mUser != null;
+    }
+
+    // TODO(b/199446283): If UserHandle.NULL is systemAPI, that can be returned here instead of null
+    /**
+     * Gets the created user handle.
+     */
+    public @Nullable UserHandle getUser() {
+        return mUser;
+    }
+
+    /**
+     * Gets operation results.
+     */
+    public @UserManager.UserOperationResult int getOperationResult() {
+        return mOperationResult;
+    }
+
+    @Override
+    public String toString() {
+        return "NewUserResponse{"
+                + "mUser="
+                + mUser
+                + ", mOperationResult=" + mOperationResult
+                + '}';
+    }
+}
diff --git a/android-34/android/os/NullVibrator.java b/android-34/android/os/NullVibrator.java
new file mode 100644
index 0000000..7859b5c
--- /dev/null
+++ b/android-34/android/os/NullVibrator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+/**
+ * Vibrator implementation that does nothing.
+ *
+ * @hide
+ */
+public class NullVibrator extends Vibrator {
+    private static final NullVibrator sInstance = new NullVibrator();
+
+    private NullVibrator() {
+    }
+
+    public static NullVibrator getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public boolean hasVibrator() {
+        return false;
+    }
+
+    @Override
+    public boolean isVibrating() {
+        return false;
+    }
+
+    @Override
+    public boolean hasAmplitudeControl() {
+        return false;
+    }
+
+    @Override
+    public void vibrate(int uid, String opPkg, VibrationEffect effect,
+            String reason, VibrationAttributes attributes) {
+    }
+
+    @Override
+    public void cancel() {
+    }
+
+    @Override
+    public void cancel(int usageFilter) {
+    }
+}
diff --git a/android-34/android/os/OperationCanceledException.java b/android-34/android/os/OperationCanceledException.java
new file mode 100644
index 0000000..b0cd663
--- /dev/null
+++ b/android-34/android/os/OperationCanceledException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+
+/**
+ * An exception type that is thrown when an operation in progress is canceled.
+ *
+ * @see CancellationSignal
+ */
+public class OperationCanceledException extends RuntimeException {
+    public OperationCanceledException() {
+        this(null);
+    }
+
+    public OperationCanceledException(String message) {
+        super(message != null ? message : "The operation has been canceled.");
+    }
+}
diff --git a/android-34/android/os/OutcomeReceiver.java b/android-34/android/os/OutcomeReceiver.java
new file mode 100644
index 0000000..4b9552e
--- /dev/null
+++ b/android-34/android/os/OutcomeReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback interface intended for use when an asynchronous operation may result in a failure.
+ *
+ * This interface may be used in cases where an asynchronous API may complete either with a value
+ * or with a {@link Throwable} that indicates an error.
+ * @param <R> The type of the result that's being sent.
+ * @param <E> The type of the {@link Throwable} that contains more information about the error.
+ */
+public interface OutcomeReceiver<R, E extends Throwable> {
+    /**
+     * Called when the asynchronous operation succeeds and delivers a result value.
+     * @param result The value delivered by the asynchronous operation.
+     */
+    void onResult(R result);
+
+    /**
+     * Called when the asynchronous operation fails. The mode of failure is indicated by the
+     * {@link Throwable} passed as an argument to this method.
+     * @param error A subclass of {@link Throwable} with more details about the error that occurred.
+     */
+    default void onError(@NonNull E error) {}
+}
diff --git a/android-34/android/os/PackageManagerPerfTest.java b/android-34/android/os/PackageManagerPerfTest.java
new file mode 100644
index 0000000..4bcc8c4
--- /dev/null
+++ b/android-34/android/os/PackageManagerPerfTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PackageManagerPerfTest {
+    private static final String PERMISSION_NAME_EXISTS =
+            "com.android.perftests.packagemanager.TestPermission";
+    private static final String PERMISSION_NAME_DOESNT_EXIST =
+            "com.android.perftests.packagemanager.TestBadPermission";
+    private static final ComponentName TEST_ACTIVITY =
+            new ComponentName("com.android.perftests.packagemanager",
+                    "android.perftests.utils.PerfTestActivity");
+    private static final String TEST_FIELD = "test";
+    private static final String TEST_VALUE = "value";
+
+    @Rule
+    public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Rule
+    public final PlatformCompatChangeRule mPlatformCompatChangeRule =
+            new PlatformCompatChangeRule();
+    @Rule
+    public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
+            InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+            Manifest.permission.INSTALL_PACKAGES,
+            Manifest.permission.DELETE_PACKAGES);
+
+    final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    private PackageInstaller mPackageInstaller;
+
+    public PackageManagerPerfTest() throws PackageManager.NameNotFoundException {
+        mPackageInstaller = mContext.getPackageManager().getPackageInstaller();
+    }
+
+    private void installTestApp(TestApp testApp) throws IOException, InterruptedException {
+        Install install = Install.single(testApp);
+        final int expectedSessionId = install.createSession();
+        PackageInstaller.Session session =
+                InstallUtils.openPackageInstallerSession(expectedSessionId);
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(TEST_FIELD, TEST_VALUE);
+        session.setAppMetadata(bundle);
+        LocalIntentSender localIntentSender = new LocalIntentSender();
+        session.commit(localIntentSender.getIntentSender());
+        Intent intent = localIntentSender.getResult();
+        InstallUtils.assertStatusSuccess(intent);
+    }
+
+    private void uninstallTestApp(String packageName) throws InterruptedException {
+        LocalIntentSender localIntentSender = new LocalIntentSender();
+        IntentSender intentSender = localIntentSender.getIntentSender();
+        mPackageInstaller.uninstall(packageName, intentSender);
+        Intent intent = localIntentSender.getResult();
+        InstallUtils.assertStatusSuccess(intent);
+    }
+
+    @Before
+    public void setup() {
+        PackageManager.disableApplicationInfoCache();
+        PackageManager.disablePackageInfoCache();
+    }
+
+    @Test
+    public void testGetAppMetadata() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        installTestApp(TestApp.A1);
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final String packageName = TestApp.A1.getPackageName();
+
+        while (state.keepRunning()) {
+            PersistableBundle bundle = pm.getAppMetadata(packageName);
+            state.pauseTiming();
+            assertEquals(bundle.size(), 1);
+            assertEquals(bundle.getString(TEST_FIELD), TEST_VALUE);
+            state.resumeTiming();
+        }
+        uninstallTestApp(packageName);
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionExists() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+
+        while (state.keepRunning()) {
+            int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionExistsWithFiltering() {
+        testCheckPermissionExists();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionDoesntExist() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+
+        while (state.keepRunning()) {
+            int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionDoesntExistWithFiltering() {
+        testCheckPermissionDoesntExist();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testQueryIntentActivities() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
+
+        while (state.keepRunning()) {
+            pm.queryIntentActivities(intent, 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testQueryIntentActivitiesWithFiltering() {
+        testQueryIntentActivities();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetPackageInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getPackageInfo(TEST_ACTIVITY.getPackageName(), 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetPackageInfoWithFiltering() throws Exception {
+        testGetPackageInfo();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetApplicationInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getApplicationInfo(TEST_ACTIVITY.getPackageName(), 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetApplicationInfoWithFiltering() throws Exception {
+        testGetApplicationInfo();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetActivityInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getActivityInfo(TEST_ACTIVITY, 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetActivityInfoWithFiltering() throws Exception {
+        testGetActivityInfo();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetInstalledPackages() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getInstalledPackages(0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetInstalledPackagesWithFiltering() throws Exception {
+        testGetInstalledPackages();
+    }
+}
diff --git a/android-34/android/os/PackageTagsList.java b/android-34/android/os/PackageTagsList.java
new file mode 100644
index 0000000..df99074
--- /dev/null
+++ b/android-34/android/os/PackageTagsList.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.Immutable;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A list of packages and associated attribution tags that supports easy membership checks. Supports
+ * "wildcard" attribution tags (ie, matching any attribution tag under a package) in additional to
+ * standard checks.
+ *
+ * @hide
+ */
+@TestApi
+@Immutable
+public final class PackageTagsList implements Parcelable {
+
+    // an empty set value matches any attribution tag (ie, wildcard)
+    private final ArrayMap<String, ArraySet<String>> mPackageTags;
+
+    private PackageTagsList(@NonNull ArrayMap<String, ArraySet<String>> packageTags) {
+        mPackageTags = Objects.requireNonNull(packageTags);
+    }
+
+    /**
+     * Returns true if this instance is empty;
+     */
+    public boolean isEmpty() {
+        return mPackageTags.isEmpty();
+    }
+
+    /**
+     * Returns true if the given package is found within this instance. If this returns true this
+     * does not imply anything about whether any given attribution tag under the given package name
+     * is present.
+     */
+    public boolean includes(@NonNull String packageName) {
+        return mPackageTags.containsKey(packageName);
+    }
+
+    /**
+     * Returns true if the given attribution tag is found within this instance under any package.
+     * Only returns true if the attribution tag literal is found, not if any package contains the
+     * set of all attribution tags.
+     *
+     * @hide
+     */
+    public boolean includesTag(@NonNull String attributionTag) {
+        final int size = mPackageTags.size();
+        for (int i = 0; i < size; i++) {
+            ArraySet<String> tags = mPackageTags.valueAt(i);
+            if (tags.contains(attributionTag)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns true if all attribution tags under the given package are contained within this
+     * instance.
+     */
+    public boolean containsAll(@NonNull String packageName) {
+        Set<String> tags = mPackageTags.get(packageName);
+        return tags != null && tags.isEmpty();
+    }
+
+    /**
+     * Returns true if the given package and attribution tag are contained within this instance.
+     */
+    public boolean contains(@NonNull String packageName, @Nullable String attributionTag) {
+        Set<String> tags = mPackageTags.get(packageName);
+        if (tags == null) {
+            return false;
+        } else if (tags.isEmpty()) {
+            // our tags are the full set, so we contain any attribution tag
+            return true;
+        } else {
+            return tags.contains(attributionTag);
+        }
+    }
+
+    /**
+     * Returns true if the given PackageTagsList is a subset of this instance.
+     */
+    public boolean contains(@NonNull PackageTagsList packageTagsList) {
+        int otherSize = packageTagsList.mPackageTags.size();
+        if (otherSize > mPackageTags.size()) {
+            return false;
+        }
+
+        for (int i = 0; i < otherSize; i++) {
+            String packageName = packageTagsList.mPackageTags.keyAt(i);
+            ArraySet<String> tags = mPackageTags.get(packageName);
+            if (tags == null) {
+                return false;
+            }
+            if (tags.isEmpty()) {
+                // our tags are the full set, so we contain whatever the other tags are
+                continue;
+            }
+            ArraySet<String> otherTags = packageTagsList.mPackageTags.valueAt(i);
+            if (otherTags.isEmpty()) {
+                // other tags are the full set, so we can't contain them
+                return false;
+            }
+            if (!tags.containsAll(otherTags)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of packages.
+     *
+     * @deprecated Do not use.
+     * @hide
+     */
+    @Deprecated
+    public @NonNull Collection<String> getPackages() {
+        return new ArrayList<>(mPackageTags.keySet());
+    }
+
+    public static final @NonNull Parcelable.Creator<PackageTagsList> CREATOR =
+            new Parcelable.Creator<PackageTagsList>() {
+                @SuppressWarnings("unchecked")
+                @Override
+                public PackageTagsList createFromParcel(Parcel in) {
+                    int count = in.readInt();
+                    ArrayMap<String, ArraySet<String>> packageTags = new ArrayMap<>(count);
+                    for (int i = 0; i < count; i++) {
+                        String key = in.readString8();
+                        ArraySet<String> value = (ArraySet<String>) in.readArraySet(null);
+                        packageTags.append(key, value);
+                    }
+                    return new PackageTagsList(packageTags);
+                }
+
+                @Override
+                public PackageTagsList[] newArray(int size) {
+                    return new PackageTagsList[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        int count = mPackageTags.size();
+        parcel.writeInt(count);
+        for (int i = 0; i < count; i++) {
+            parcel.writeString8(mPackageTags.keyAt(i));
+            parcel.writeArraySet(mPackageTags.valueAt(i));
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof PackageTagsList)) {
+            return false;
+        }
+
+        PackageTagsList that = (PackageTagsList) o;
+        return mPackageTags.equals(that.mPackageTags);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageTags);
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return mPackageTags.toString();
+    }
+
+    /**
+     * @hide
+     */
+    public void dump(PrintWriter pw) {
+        int size = mPackageTags.size();
+        for (int i = 0; i < size; i++) {
+            String packageName = mPackageTags.keyAt(i);
+            pw.print(packageName);
+            pw.print("[");
+            int tagsSize = mPackageTags.valueAt(i).size();
+            if (tagsSize == 0) {
+                pw.print("*");
+            } else {
+                for (int j = 0; j < tagsSize; j++) {
+                    String attributionTag = mPackageTags.valueAt(i).valueAt(j);
+                    if (j > 0) {
+                        pw.print(", ");
+                    }
+                    if (attributionTag != null && attributionTag.startsWith(packageName)) {
+                        pw.print(attributionTag.substring(packageName.length()));
+                    } else {
+                        pw.print(attributionTag);
+                    }
+                }
+            }
+            pw.println("]");
+        }
+    }
+
+    /**
+     * Builder class for {@link PackageTagsList}.
+     */
+    public static final class Builder {
+
+        private final ArrayMap<String, ArraySet<String>> mPackageTags;
+
+        /**
+         * Creates a new builder.
+         */
+        public Builder() {
+            mPackageTags = new ArrayMap<>();
+        }
+
+        /**
+         * Creates a new builder with the given initial capacity.
+         */
+        public Builder(int capacity) {
+            mPackageTags = new ArrayMap<>(capacity);
+        }
+
+        /**
+         * Adds all attribution tags under the specified package to the builder.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder add(@NonNull String packageName) {
+            mPackageTags.computeIfAbsent(packageName, p -> new ArraySet<>()).clear();
+            return this;
+        }
+
+        /**
+         * Adds the specified package and attribution tag to the builder.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder add(@NonNull String packageName, @Nullable String attributionTag) {
+            ArraySet<String> tags = mPackageTags.get(packageName);
+            if (tags == null) {
+                tags = new ArraySet<>(1);
+                tags.add(attributionTag);
+                mPackageTags.put(packageName, tags);
+            } else if (!tags.isEmpty()) {
+                tags.add(attributionTag);
+            }
+
+            return this;
+        }
+
+        /**
+         * Adds the specified package and set of attribution tags to the builder.
+         *
+         * @hide
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder add(@NonNull String packageName,
+                @NonNull Collection<String> attributionTags) {
+            if (attributionTags.isEmpty()) {
+                // the input is not allowed to specify a full set by passing in an empty collection
+                return this;
+            }
+
+            ArraySet<String> tags = mPackageTags.get(packageName);
+            if (tags == null) {
+                tags = new ArraySet<>(attributionTags);
+                mPackageTags.put(packageName, tags);
+            } else if (!tags.isEmpty()) {
+                // if we contain the full set, already done, otherwise add all the tags
+                tags.addAll(attributionTags);
+            }
+
+            return this;
+        }
+
+        /**
+         * Adds the specified {@link PackageTagsList} to the builder.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder add(@NonNull PackageTagsList packageTagsList) {
+            return add(packageTagsList.mPackageTags);
+        }
+
+        /**
+         * Adds the given map of package to attribution tags to the builder. An empty set of
+         * attribution tags is interpreted to imply all attribution tags under that package.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder add(@NonNull Map<String, ? extends Set<String>> packageTagsMap) {
+            mPackageTags.ensureCapacity(packageTagsMap.size());
+            for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) {
+                Set<String> newTags = entry.getValue();
+                if (newTags.isEmpty()) {
+                    add(entry.getKey());
+                } else {
+                    add(entry.getKey(), newTags);
+                }
+            }
+
+            return this;
+        }
+
+        /**
+         * Removes all attribution tags under the specified package from the builder.
+         *
+         * @hide
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder remove(@NonNull String packageName) {
+            mPackageTags.remove(packageName);
+            return this;
+        }
+
+        /**
+         * Removes the specified package and attribution tag from the builder if and only if the
+         * specified attribution tag is listed explicitly under the package. If the package contains
+         * all possible attribution tags, then nothing will be removed.
+         *
+         * @hide
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder remove(@NonNull String packageName,
+                @Nullable String attributionTag) {
+            ArraySet<String> tags = mPackageTags.get(packageName);
+            if (tags != null && tags.remove(attributionTag) && tags.isEmpty()) {
+                mPackageTags.remove(packageName);
+            }
+            return this;
+        }
+
+        /**
+         * Removes the specified package and set of attribution tags from the builder if and only if
+         * the specified set of attribution tags are listed explicitly under the package. If the
+         * package contains all possible attribution tags, then nothing will be removed.
+         *
+         * @hide
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder remove(@NonNull String packageName,
+                @NonNull Collection<String> attributionTags) {
+            if (attributionTags.isEmpty()) {
+                // the input is not allowed to specify a full set by passing in an empty collection
+                return this;
+            }
+
+            ArraySet<String> tags = mPackageTags.get(packageName);
+            if (tags != null && tags.removeAll(attributionTags) && tags.isEmpty()) {
+                mPackageTags.remove(packageName);
+            }
+            return this;
+        }
+
+        /**
+         * Removes the specified {@link PackageTagsList} from the builder. If a package contains all
+         * possible attribution tags, it will only be removed if the package in the removed
+         * {@link PackageTagsList} also contains all possible attribution tags.
+         *
+         * @hide
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder remove(@NonNull PackageTagsList packageTagsList) {
+            return remove(packageTagsList.mPackageTags);
+        }
+
+        /**
+         * Removes the given map of package to attribution tags to the builder. An empty set of
+         * attribution tags is interpreted to imply all attribution tags under that package. If a
+         * package contains all possible attribution tags, it will only be removed if the package in
+         * the removed map also contains all possible attribution tags.
+         *
+         * @hide
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder remove(@NonNull Map<String, ? extends Set<String>> packageTagsMap) {
+            for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) {
+                Set<String> removedTags = entry.getValue();
+                if (removedTags.isEmpty()) {
+                    // if removing the full set, drop the package completely
+                    remove(entry.getKey());
+                } else {
+                    remove(entry.getKey(), removedTags);
+                }
+            }
+
+            return this;
+        }
+
+        /**
+         * Clears the builder.
+         */
+        public @NonNull Builder clear() {
+            mPackageTags.clear();
+            return this;
+        }
+
+        /**
+         * Constructs a new {@link PackageTagsList}.
+         */
+        public @NonNull PackageTagsList build() {
+            return new PackageTagsList(copy(mPackageTags));
+        }
+
+        private static ArrayMap<String, ArraySet<String>> copy(
+                ArrayMap<String, ArraySet<String>> value) {
+            int size = value.size();
+            ArrayMap<String, ArraySet<String>> copy = new ArrayMap<>(size);
+            for (int i = 0; i < size; i++) {
+                String packageName = value.keyAt(i);
+                ArraySet<String> tags = new ArraySet<>(Objects.requireNonNull(value.valueAt(i)));
+                copy.append(packageName, tags);
+            }
+            return copy;
+        }
+    }
+}
diff --git a/android-34/android/os/Parcel.java b/android-34/android/os/Parcel.java
new file mode 100644
index 0000000..e784c26
--- /dev/null
+++ b/android-34/android/os/Parcel.java
@@ -0,0 +1,5552 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.app.AppOpsManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.ExceptionUtils;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.Pair;
+import android.util.Size;
+import android.util.SizeF;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.SneakyThrow;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+
+/**
+ * Container for a message (data and object references) that can
+ * be sent through an IBinder.  A Parcel can contain both flattened data
+ * that will be unflattened on the other side of the IPC (using the various
+ * methods here for writing specific types, or the general
+ * {@link Parcelable} interface), and references to live {@link IBinder}
+ * objects that will result in the other side receiving a proxy IBinder
+ * connected with the original IBinder in the Parcel.
+ *
+ * <p class="note">Parcel is <strong>not</strong> a general-purpose
+ * serialization mechanism.  This class (and the corresponding
+ * {@link Parcelable} API for placing arbitrary objects into a Parcel) is
+ * designed as a high-performance IPC transport.  As such, it is not
+ * appropriate to place any Parcel data in to persistent storage: changes
+ * in the underlying implementation of any of the data in the Parcel can
+ * render older data unreadable.</p>
+ *
+ * <p>The bulk of the Parcel API revolves around reading and writing data
+ * of various types.  There are six major classes of such functions available.</p>
+ *
+ * <h3>Primitives</h3>
+ *
+ * <p>The most basic data functions are for writing and reading primitive
+ * data types: {@link #writeByte}, {@link #readByte}, {@link #writeDouble},
+ * {@link #readDouble}, {@link #writeFloat}, {@link #readFloat}, {@link #writeInt},
+ * {@link #readInt}, {@link #writeLong}, {@link #readLong},
+ * {@link #writeString}, {@link #readString}.  Most other
+ * data operations are built on top of these.  The given data is written and
+ * read using the endianess of the host CPU.</p>
+ *
+ * <h3>Primitive Arrays</h3>
+ *
+ * <p>There are a variety of methods for reading and writing raw arrays
+ * of primitive objects, which generally result in writing a 4-byte length
+ * followed by the primitive data items.  The methods for reading can either
+ * read the data into an existing array, or create and return a new array.
+ * These available types are:</p>
+ *
+ * <ul>
+ * <li> {@link #writeBooleanArray(boolean[])},
+ * {@link #readBooleanArray(boolean[])}, {@link #createBooleanArray()}
+ * <li> {@link #writeByteArray(byte[])},
+ * {@link #writeByteArray(byte[], int, int)}, {@link #readByteArray(byte[])},
+ * {@link #createByteArray()}
+ * <li> {@link #writeCharArray(char[])}, {@link #readCharArray(char[])},
+ * {@link #createCharArray()}
+ * <li> {@link #writeDoubleArray(double[])}, {@link #readDoubleArray(double[])},
+ * {@link #createDoubleArray()}
+ * <li> {@link #writeFloatArray(float[])}, {@link #readFloatArray(float[])},
+ * {@link #createFloatArray()}
+ * <li> {@link #writeIntArray(int[])}, {@link #readIntArray(int[])},
+ * {@link #createIntArray()}
+ * <li> {@link #writeLongArray(long[])}, {@link #readLongArray(long[])},
+ * {@link #createLongArray()}
+ * <li> {@link #writeStringArray(String[])}, {@link #readStringArray(String[])},
+ * {@link #createStringArray()}.
+ * <li> {@link #writeSparseBooleanArray(SparseBooleanArray)},
+ * {@link #readSparseBooleanArray()}.
+ * </ul>
+ *
+ * <h3>Parcelables</h3>
+ *
+ * <p>The {@link Parcelable} protocol provides an extremely efficient (but
+ * low-level) protocol for objects to write and read themselves from Parcels.
+ * You can use the direct methods {@link #writeParcelable(Parcelable, int)}
+ * and {@link #readParcelable(ClassLoader)} or
+ * {@link #writeParcelableArray} and
+ * {@link #readParcelableArray(ClassLoader)} to write or read.  These
+ * methods write both the class type and its data to the Parcel, allowing
+ * that class to be reconstructed from the appropriate class loader when
+ * later reading.</p>
+ *
+ * <p>There are also some methods that provide a more efficient way to work
+ * with Parcelables: {@link #writeTypedObject}, {@link #writeTypedArray},
+ * {@link #writeTypedList}, {@link #readTypedObject},
+ * {@link #createTypedArray} and {@link #createTypedArrayList}.  These methods
+ * do not write the class information of the original object: instead, the
+ * caller of the read function must know what type to expect and pass in the
+ * appropriate {@link Parcelable.Creator Parcelable.Creator} instead to
+ * properly construct the new object and read its data.  (To more efficient
+ * write and read a single Parcelable object that is not null, you can directly
+ * call {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
+ * {@link Parcelable.Creator#createFromParcel Parcelable.Creator.createFromParcel}
+ * yourself.)</p>
+ *
+ * <h3>Bundles</h3>
+ *
+ * <p>A special type-safe container, called {@link Bundle}, is available
+ * for key/value maps of heterogeneous values.  This has many optimizations
+ * for improved performance when reading and writing data, and its type-safe
+ * API avoids difficult to debug type errors when finally marshalling the
+ * data contents into a Parcel.  The methods to use are
+ * {@link #writeBundle(Bundle)}, {@link #readBundle()}, and
+ * {@link #readBundle(ClassLoader)}.
+ *
+ * <h3>Active Objects</h3>
+ *
+ * <p>An unusual feature of Parcel is the ability to read and write active
+ * objects.  For these objects the actual contents of the object is not
+ * written, rather a special token referencing the object is written.  When
+ * reading the object back from the Parcel, you do not get a new instance of
+ * the object, but rather a handle that operates on the exact same object that
+ * was originally written.  There are two forms of active objects available.</p>
+ *
+ * <p>{@link Binder} objects are a core facility of Android's general cross-process
+ * communication system.  The {@link IBinder} interface describes an abstract
+ * protocol with a Binder object.  Any such interface can be written in to
+ * a Parcel, and upon reading you will receive either the original object
+ * implementing that interface or a special proxy implementation
+ * that communicates calls back to the original object.  The methods to use are
+ * {@link #writeStrongBinder(IBinder)},
+ * {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()},
+ * {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])},
+ * {@link #createBinderArray()},
+ * {@link #writeInterfaceArray(T[])}, {@link #readInterfaceArray(T[], Function)},
+ * {@link #createInterfaceArray(IntFunction, Function)},
+ * {@link #writeBinderList(List)}, {@link #readBinderList(List)},
+ * {@link #createBinderArrayList()},
+ * {@link #writeInterfaceList(List)}, {@link #readInterfaceList(List, Function)},
+ * {@link #createInterfaceArrayList(Function)}.</p>
+ *
+ * <p>FileDescriptor objects, representing raw Linux file descriptor identifiers,
+ * can be written and {@link ParcelFileDescriptor} objects returned to operate
+ * on the original file descriptor.  The returned file descriptor is a dup
+ * of the original file descriptor: the object and fd is different, but
+ * operating on the same underlying file stream, with the same position, etc.
+ * The methods to use are {@link #writeFileDescriptor(FileDescriptor)},
+ * {@link #readFileDescriptor()}.
+ *
+  * <h3>Parcelable Containers</h3>
+ *
+ * <p>A final class of methods are for writing and reading standard Java
+ * containers of arbitrary types.  These all revolve around the
+ * {@link #writeValue(Object)} and {@link #readValue(ClassLoader)} methods
+ * which define the types of objects allowed.  The container methods are
+ * {@link #writeArray(Object[])}, {@link #readArray(ClassLoader)},
+ * {@link #writeList(List)}, {@link #readList(List, ClassLoader)},
+ * {@link #readArrayList(ClassLoader)},
+ * {@link #writeMap(Map)}, {@link #readMap(Map, ClassLoader)},
+ * {@link #writeSparseArray(SparseArray)},
+ * {@link #readSparseArray(ClassLoader)}.
+ *
+ * <h3>Restricted Parcelable Containers</h3>
+ *
+ * <p>A final class of methods are for reading standard Java containers of restricted types.
+ * These methods replace methods for reading containers of arbitrary types from previous section
+ * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. The pairing writing methods are
+ * still the same from previous section.
+ * These methods accepts additional {@code clazz} parameters as the required types.
+ * The Restricted Parcelable container methods are {@link #readArray(ClassLoader, Class)},
+ * {@link #readList(List, ClassLoader, Class)},
+ * {@link #readArrayList(ClassLoader, Class)},
+ * {@link #readMap(Map, ClassLoader, Class, Class)},
+ * {@link #readSparseArray(ClassLoader, Class)}.
+ */
+public final class Parcel {
+
+    private static final boolean DEBUG_RECYCLE = false;
+    private static final boolean DEBUG_ARRAY_MAP = false;
+    private static final String TAG = "Parcel";
+
+    @UnsupportedAppUsage
+    @SuppressWarnings({"UnusedDeclaration"})
+    private long mNativePtr; // used by native code
+
+    /**
+     * Flag indicating if {@link #mNativePtr} was allocated by this object,
+     * indicating that we're responsible for its lifecycle.
+     */
+    private boolean mOwnsNativeParcelObject;
+    private long mNativeSize;
+
+    private ArrayMap<Class, Object> mClassCookies;
+
+    private RuntimeException mStack;
+    private boolean mRecycled = false;
+
+    /** @hide */
+    @TestApi
+    public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1 << 0;
+
+    /** @hide */
+    @TestApi
+    public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 1 << 1;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT,
+            FLAG_PROPAGATE_ALLOW_BLOCKING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ParcelFlags {}
+
+    @ParcelFlags
+    private int mFlags;
+
+    /**
+     * Whether or not to parcel the stack trace of an exception. This has a performance
+     * impact, so should only be included in specific processes and only on debug builds.
+     */
+    private static boolean sParcelExceptionStackTrace;
+
+    private static final Object sPoolSync = new Object();
+
+    /** Next item in the linked list pool, if any */
+    @GuardedBy("sPoolSync")
+    private Parcel mPoolNext;
+
+    /** Head of a linked list pool of {@link Parcel} objects */
+    @GuardedBy("sPoolSync")
+    private static Parcel sOwnedPool;
+    /** Head of a linked list pool of {@link Parcel} objects */
+    @GuardedBy("sPoolSync")
+    private static Parcel sHolderPool;
+
+    /** Total size of pool with head at {@link #sOwnedPool} */
+    @GuardedBy("sPoolSync")
+    private static int sOwnedPoolSize = 0;
+    /** Total size of pool with head at {@link #sHolderPool} */
+    @GuardedBy("sPoolSync")
+    private static int sHolderPoolSize = 0;
+
+    /**
+     * We're willing to pool up to 32 objects, which is sized to accommodate
+     * both a data and reply Parcel for the maximum of 16 Binder threads.
+     */
+    private static final int POOL_SIZE = 32;
+
+    // Keep in sync with frameworks/native/include/private/binder/ParcelValTypes.h.
+    private static final int VAL_NULL = -1;
+    private static final int VAL_STRING = 0;
+    private static final int VAL_INTEGER = 1;
+    private static final int VAL_MAP = 2; // length-prefixed
+    private static final int VAL_BUNDLE = 3;
+    private static final int VAL_PARCELABLE = 4; // length-prefixed
+    private static final int VAL_SHORT = 5;
+    private static final int VAL_LONG = 6;
+    private static final int VAL_FLOAT = 7;
+    private static final int VAL_DOUBLE = 8;
+    private static final int VAL_BOOLEAN = 9;
+    private static final int VAL_CHARSEQUENCE = 10;
+    private static final int VAL_LIST  = 11; // length-prefixed
+    private static final int VAL_SPARSEARRAY = 12; // length-prefixed
+    private static final int VAL_BYTEARRAY = 13;
+    private static final int VAL_STRINGARRAY = 14;
+    private static final int VAL_IBINDER = 15;
+    private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed
+    private static final int VAL_OBJECTARRAY = 17; // length-prefixed
+    private static final int VAL_INTARRAY = 18;
+    private static final int VAL_LONGARRAY = 19;
+    private static final int VAL_BYTE = 20;
+    private static final int VAL_SERIALIZABLE = 21; // length-prefixed
+    private static final int VAL_SPARSEBOOLEANARRAY = 22;
+    private static final int VAL_BOOLEANARRAY = 23;
+    private static final int VAL_CHARSEQUENCEARRAY = 24;
+    private static final int VAL_PERSISTABLEBUNDLE = 25;
+    private static final int VAL_SIZE = 26;
+    private static final int VAL_SIZEF = 27;
+    private static final int VAL_DOUBLEARRAY = 28;
+    private static final int VAL_CHAR = 29;
+    private static final int VAL_SHORTARRAY = 30;
+    private static final int VAL_CHARARRAY = 31;
+    private static final int VAL_FLOATARRAY = 32;
+
+    // The initial int32 in a Binder call's reply Parcel header:
+    // Keep these in sync with libbinder's binder/Status.h.
+    private static final int EX_SECURITY = -1;
+    private static final int EX_BAD_PARCELABLE = -2;
+    private static final int EX_ILLEGAL_ARGUMENT = -3;
+    private static final int EX_NULL_POINTER = -4;
+    private static final int EX_ILLEGAL_STATE = -5;
+    private static final int EX_NETWORK_MAIN_THREAD = -6;
+    private static final int EX_UNSUPPORTED_OPERATION = -7;
+    private static final int EX_SERVICE_SPECIFIC = -8;
+    private static final int EX_PARCELABLE = -9;
+    /** @hide */
+    // WARNING: DO NOT add more 'reply' headers. These also need to add work to native
+    // code and this encodes extra information in object statuses. If we need to expand
+    // this design, we should add a generic way to attach parcelables/structured parcelables
+    // to transactions which can work across languages.
+    public static final int EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127; // special; see below
+    // WARNING: DO NOT add more 'reply' headers. These also need to add work to native
+    // code and this encodes extra information in object statuses. If we need to expand
+    // this design, we should add a generic way to attach parcelables/structured parcelables
+    // to transactions which can work across languages.
+    private static final int EX_HAS_STRICTMODE_REPLY_HEADER = -128;  // special; see below
+    // EX_TRANSACTION_FAILED is used exclusively in native code.
+    // see libbinder's binder/Status.h
+    private static final int EX_TRANSACTION_FAILED = -129;
+
+    @CriticalNative
+    private static native void nativeMarkSensitive(long nativePtr);
+    @FastNative
+    private static native void nativeMarkForBinder(long nativePtr, IBinder binder);
+    @CriticalNative
+    private static native boolean nativeIsForRpc(long nativePtr);
+    @CriticalNative
+    private static native int nativeDataSize(long nativePtr);
+    @CriticalNative
+    private static native int nativeDataAvail(long nativePtr);
+    @CriticalNative
+    private static native int nativeDataPosition(long nativePtr);
+    @CriticalNative
+    private static native int nativeDataCapacity(long nativePtr);
+    @FastNative
+    private static native void nativeSetDataSize(long nativePtr, int size);
+    @CriticalNative
+    private static native void nativeSetDataPosition(long nativePtr, int pos);
+    @FastNative
+    private static native void nativeSetDataCapacity(long nativePtr, int size);
+
+    @CriticalNative
+    private static native boolean nativePushAllowFds(long nativePtr, boolean allowFds);
+    @CriticalNative
+    private static native void nativeRestoreAllowFds(long nativePtr, boolean lastValue);
+
+    private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len);
+    private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len);
+    @CriticalNative
+    private static native int nativeWriteInt(long nativePtr, int val);
+    @CriticalNative
+    private static native int nativeWriteLong(long nativePtr, long val);
+    @CriticalNative
+    private static native int nativeWriteFloat(long nativePtr, float val);
+    @CriticalNative
+    private static native int nativeWriteDouble(long nativePtr, double val);
+    private static native void nativeSignalExceptionForError(int error);
+    @FastNative
+    private static native void nativeWriteString8(long nativePtr, String val);
+    @FastNative
+    private static native void nativeWriteString16(long nativePtr, String val);
+    @FastNative
+    private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
+    @FastNative
+    private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+
+    private static native byte[] nativeCreateByteArray(long nativePtr);
+    private static native boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen);
+    private static native byte[] nativeReadBlob(long nativePtr);
+    @CriticalNative
+    private static native int nativeReadInt(long nativePtr);
+    @CriticalNative
+    private static native long nativeReadLong(long nativePtr);
+    @CriticalNative
+    private static native float nativeReadFloat(long nativePtr);
+    @CriticalNative
+    private static native double nativeReadDouble(long nativePtr);
+    @FastNative
+    private static native String nativeReadString8(long nativePtr);
+    @FastNative
+    private static native String nativeReadString16(long nativePtr);
+    @FastNative
+    private static native IBinder nativeReadStrongBinder(long nativePtr);
+    @FastNative
+    private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
+
+    private static native long nativeCreate();
+    private static native void nativeFreeBuffer(long nativePtr);
+    private static native void nativeDestroy(long nativePtr);
+
+    private static native byte[] nativeMarshall(long nativePtr);
+    private static native void nativeUnmarshall(
+            long nativePtr, byte[] data, int offset, int length);
+    private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
+    private static native boolean nativeCompareDataInRange(
+            long ptrA, int offsetA, long ptrB, int offsetB, int length);
+    private static native void nativeAppendFrom(
+            long thisNativePtr, long otherNativePtr, int offset, int length);
+    @CriticalNative
+    private static native boolean nativeHasFileDescriptors(long nativePtr);
+    private static native boolean nativeHasFileDescriptorsInRange(
+            long nativePtr, int offset, int length);
+    private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
+    private static native void nativeEnforceInterface(long nativePtr, String interfaceName);
+
+    @CriticalNative
+    private static native boolean nativeReplaceCallingWorkSourceUid(
+            long nativePtr, int workSourceUid);
+    @CriticalNative
+    private static native int nativeReadCallingWorkSourceUid(long nativePtr);
+
+    /** Last time exception with a stack trace was written */
+    private static volatile long sLastWriteExceptionStackTrace;
+    /** Used for throttling of writing stack trace, which is costly */
+    private static final int WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS = 1000;
+
+    @CriticalNative
+    private static native long nativeGetOpenAshmemSize(long nativePtr);
+
+    public final static Parcelable.Creator<String> STRING_CREATOR
+             = new Parcelable.Creator<String>() {
+        public String createFromParcel(Parcel source) {
+            return source.readString();
+        }
+        public String[] newArray(int size) {
+            return new String[size];
+        }
+    };
+
+    /**
+     * @hide
+     */
+    public static class ReadWriteHelper {
+
+        @UnsupportedAppUsage
+        public ReadWriteHelper() {
+        }
+
+        public static final ReadWriteHelper DEFAULT = new ReadWriteHelper();
+
+        /**
+         * Called when writing a string to a parcel. Subclasses wanting to write a string
+         * must use {@link #writeStringNoHelper(String)} to avoid
+         * infinity recursive calls.
+         */
+        public void writeString8(Parcel p, String s) {
+            p.writeString8NoHelper(s);
+        }
+
+        public void writeString16(Parcel p, String s) {
+            p.writeString16NoHelper(s);
+        }
+
+        /**
+         * Called when reading a string to a parcel. Subclasses wanting to read a string
+         * must use {@link #readStringNoHelper()} to avoid
+         * infinity recursive calls.
+         */
+        public String readString8(Parcel p) {
+            return p.readString8NoHelper();
+        }
+
+        public String readString16(Parcel p) {
+            return p.readString16NoHelper();
+        }
+    }
+
+    private ReadWriteHelper mReadWriteHelper = ReadWriteHelper.DEFAULT;
+
+    /**
+     * Retrieve a new Parcel object from the pool.
+     */
+    @NonNull
+    public static Parcel obtain() {
+        Parcel res = null;
+        synchronized (sPoolSync) {
+            if (sOwnedPool != null) {
+                res = sOwnedPool;
+                sOwnedPool = res.mPoolNext;
+                res.mPoolNext = null;
+                sOwnedPoolSize--;
+            }
+        }
+
+        // When no cache found above, create from scratch; otherwise prepare the
+        // cached object to be used
+        if (res == null) {
+            res = new Parcel(0);
+        } else {
+            res.mRecycled = false;
+            if (DEBUG_RECYCLE) {
+                res.mStack = new RuntimeException();
+            }
+            res.mReadWriteHelper = ReadWriteHelper.DEFAULT;
+        }
+        return res;
+    }
+
+    /**
+     * Retrieve a new Parcel object from the pool for use with a specific binder.
+     *
+     * Associate this parcel with a binder object. This marks the parcel as being prepared for a
+     * transaction on this specific binder object. Based on this, the format of the wire binder
+     * protocol may change. For future compatibility, it is recommended to use this for all
+     * Parcels.
+     */
+    @NonNull
+    public static Parcel obtain(@NonNull IBinder binder) {
+        Parcel parcel = Parcel.obtain();
+        parcel.markForBinder(binder);
+        return parcel;
+    }
+
+    /**
+     * Put a Parcel object back into the pool.  You must not touch
+     * the object after this call.
+     */
+    public final void recycle() {
+        if (mRecycled) {
+            Log.wtf(TAG, "Recycle called on unowned Parcel. (recycle twice?) Here: "
+                    + Log.getStackTraceString(new Throwable())
+                    + " Original recycle call (if DEBUG_RECYCLE): ", mStack);
+
+            return;
+        }
+        mRecycled = true;
+
+        // We try to reset the entire object here, but in order to be
+        // able to print a stack when a Parcel is recycled twice, that
+        // is cleared in obtain instead.
+
+        mClassCookies = null;
+        freeBuffer();
+
+        if (mOwnsNativeParcelObject) {
+            synchronized (sPoolSync) {
+                if (sOwnedPoolSize < POOL_SIZE) {
+                    mPoolNext = sOwnedPool;
+                    sOwnedPool = this;
+                    sOwnedPoolSize++;
+                }
+            }
+        } else {
+            mNativePtr = 0;
+            synchronized (sPoolSync) {
+                if (sHolderPoolSize < POOL_SIZE) {
+                    mPoolNext = sHolderPool;
+                    sHolderPool = this;
+                    sHolderPoolSize++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Set a {@link ReadWriteHelper}, which can be used to avoid having duplicate strings, for
+     * example.
+     *
+     * @hide
+     */
+    public void setReadWriteHelper(@Nullable ReadWriteHelper helper) {
+        mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT;
+    }
+
+    /**
+     * @return whether this parcel has a {@link ReadWriteHelper}.
+     *
+     * @hide
+     */
+    public boolean hasReadWriteHelper() {
+        return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static native long getGlobalAllocSize();
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static native long getGlobalAllocCount();
+
+    /**
+     * Parcel data should be zero'd before realloc'd or deleted.
+     *
+     * Note: currently this feature requires multiple things to work in concert:
+     * - markSensitive must be called on every relative Parcel
+     * - FLAG_CLEAR_BUF must be passed into the kernel
+     * This requires having code which does the right thing in every method and in every backend
+     * of AIDL. Rather than exposing this API, it should be replaced with a single API on
+     * IBinder objects which can be called once, and the information should be fed into the
+     * Parcel using markForBinder APIs. In terms of code size and number of API calls, this is
+     * much more extensible.
+     *
+     * @hide
+     */
+    public final void markSensitive() {
+        nativeMarkSensitive(mNativePtr);
+    }
+
+    /**
+     * @hide
+     */
+    private void markForBinder(@NonNull IBinder binder) {
+        nativeMarkForBinder(mNativePtr, binder);
+    }
+
+    /**
+     * Whether this Parcel is written for an RPC transaction.
+     *
+     * @hide
+     */
+    public final boolean isForRpc() {
+        return nativeIsForRpc(mNativePtr);
+    }
+
+    /** @hide */
+    @ParcelFlags
+    @TestApi
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /** @hide */
+    public void setFlags(@ParcelFlags int flags) {
+        mFlags = flags;
+    }
+
+    /** @hide */
+    public void addFlags(@ParcelFlags int flags) {
+        mFlags |= flags;
+    }
+
+    /** @hide */
+    private boolean hasFlags(@ParcelFlags int flags) {
+        return (mFlags & flags) == flags;
+    }
+
+    /**
+     * This method is used by the AIDL compiler for system components. Not intended to be
+     * used by non-system apps.
+     */
+    // Note: Ideally this method should be @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES),
+    // but we need to make this method public due to the way the aidl compiler is compiled.
+    // We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
+    // This would only work when used on a reply parcel by a binder object that's allowed-blocking.
+    public void setPropagateAllowBlocking() {
+        addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
+    }
+
+    /**
+     * Returns the total amount of data contained in the parcel.
+     */
+    public int dataSize() {
+        return nativeDataSize(mNativePtr);
+    }
+
+    /**
+     * Returns the amount of data remaining to be read from the
+     * parcel.  That is, {@link #dataSize}-{@link #dataPosition}.
+     */
+    public final int dataAvail() {
+        return nativeDataAvail(mNativePtr);
+    }
+
+    /**
+     * Returns the current position in the parcel data.  Never
+     * more than {@link #dataSize}.
+     */
+    public final int dataPosition() {
+        return nativeDataPosition(mNativePtr);
+    }
+
+    /**
+     * Returns the total amount of space in the parcel.  This is always
+     * >= {@link #dataSize}.  The difference between it and dataSize() is the
+     * amount of room left until the parcel needs to re-allocate its
+     * data buffer.
+     */
+    public final int dataCapacity() {
+        return nativeDataCapacity(mNativePtr);
+    }
+
+    /**
+     * Change the amount of data in the parcel.  Can be either smaller or
+     * larger than the current size.  If larger than the current capacity,
+     * more memory will be allocated.
+     *
+     * @param size The new number of bytes in the Parcel.
+     */
+    public final void setDataSize(int size) {
+        nativeSetDataSize(mNativePtr, size);
+    }
+
+    /**
+     * Move the current read/write position in the parcel.
+     * @param pos New offset in the parcel; must be between 0 and
+     * {@link #dataSize}.
+     */
+    public final void setDataPosition(int pos) {
+        nativeSetDataPosition(mNativePtr, pos);
+    }
+
+    /**
+     * Change the capacity (current available space) of the parcel.
+     *
+     * @param size The new capacity of the parcel, in bytes.  Can not be
+     * less than {@link #dataSize} -- that is, you can not drop existing data
+     * with this method.
+     */
+    public final void setDataCapacity(int size) {
+        nativeSetDataCapacity(mNativePtr, size);
+    }
+
+    /** @hide */
+    public final boolean pushAllowFds(boolean allowFds) {
+        return nativePushAllowFds(mNativePtr, allowFds);
+    }
+
+    /** @hide */
+    public final void restoreAllowFds(boolean lastValue) {
+        nativeRestoreAllowFds(mNativePtr, lastValue);
+    }
+
+    /**
+     * Returns the raw bytes of the parcel.
+     *
+     * <p class="note">The data you retrieve here <strong>must not</strong>
+     * be placed in any kind of persistent storage (on local disk, across
+     * a network, etc).  For that, you should use standard serialization
+     * or another kind of general serialization mechanism.  The Parcel
+     * marshalled representation is highly optimized for local IPC, and as
+     * such does not attempt to maintain compatibility with data created
+     * in different versions of the platform.
+     */
+    public final byte[] marshall() {
+        return nativeMarshall(mNativePtr);
+    }
+
+    /**
+     * Fills the raw bytes of this Parcel with the supplied data.
+     */
+    public final void unmarshall(@NonNull byte[] data, int offset, int length) {
+        nativeUnmarshall(mNativePtr, data, offset, length);
+    }
+
+    public final void appendFrom(Parcel parcel, int offset, int length) {
+        nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
+    }
+
+    /** @hide */
+    public int compareData(Parcel other) {
+        return nativeCompareData(mNativePtr, other.mNativePtr);
+    }
+
+    /** @hide */
+    public static boolean compareData(Parcel a, int offsetA, Parcel b, int offsetB, int length) {
+        return nativeCompareDataInRange(a.mNativePtr, offsetA, b.mNativePtr, offsetB, length);
+    }
+
+    /** @hide */
+    public final void setClassCookie(Class clz, Object cookie) {
+        if (mClassCookies == null) {
+            mClassCookies = new ArrayMap<>();
+        }
+        mClassCookies.put(clz, cookie);
+    }
+
+    /** @hide */
+    @Nullable
+    public final Object getClassCookie(Class clz) {
+        return mClassCookies != null ? mClassCookies.get(clz) : null;
+    }
+
+    /** @hide */
+    public final void adoptClassCookies(Parcel from) {
+        mClassCookies = from.mClassCookies;
+    }
+
+    /** @hide */
+    public Map<Class, Object> copyClassCookies() {
+        return new ArrayMap<>(mClassCookies);
+    }
+
+    /** @hide */
+    public void putClassCookies(Map<Class, Object> cookies) {
+        if (cookies == null) {
+            return;
+        }
+        if (mClassCookies == null) {
+            mClassCookies = new ArrayMap<>();
+        }
+        mClassCookies.putAll(cookies);
+    }
+
+    /**
+     * Report whether the parcel contains any marshalled file descriptors.
+     */
+    public boolean hasFileDescriptors() {
+        return nativeHasFileDescriptors(mNativePtr);
+    }
+
+    /**
+     * Report whether the parcel contains any marshalled file descriptors in the range defined by
+     * {@code offset} and {@code length}.
+     *
+     * @param offset The offset from which the range starts. Should be between 0 and
+     *     {@link #dataSize()}.
+     * @param length The length of the range. Should be between 0 and {@link #dataSize()} - {@code
+     *     offset}.
+     * @return whether there are file descriptors or not.
+     * @throws IllegalArgumentException if the parameters are out of the permitted ranges.
+     */
+    public boolean hasFileDescriptors(int offset, int length) {
+        return nativeHasFileDescriptorsInRange(mNativePtr, offset, length);
+    }
+
+    /**
+     * Check if the object has file descriptors.
+     *
+     * <p>Objects supported are {@link Parcel} and objects that can be passed to {@link
+     * #writeValue(Object)}}
+     *
+     * <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method
+     * for that.
+     *
+     * @throws IllegalArgumentException if you provide any object not supported by above methods
+     *     (including if the unsupported object is inside a nested container).
+     *
+     * @hide
+     */
+    public static boolean hasFileDescriptors(Object value) {
+        if (value instanceof Parcel) {
+            Parcel parcel = (Parcel) value;
+            if (parcel.hasFileDescriptors()) {
+                return true;
+            }
+        } else if (value instanceof LazyValue) {
+            LazyValue lazy = (LazyValue) value;
+            if (lazy.hasFileDescriptors()) {
+                return true;
+            }
+        } else if (value instanceof Parcelable) {
+            Parcelable parcelable = (Parcelable) value;
+            if ((parcelable.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+                return true;
+            }
+        } else if (value instanceof ArrayMap<?, ?>) {
+            ArrayMap<?, ?> map = (ArrayMap<?, ?>) value;
+            for (int i = 0, n = map.size(); i < n; i++) {
+                if (hasFileDescriptors(map.keyAt(i))
+                        || hasFileDescriptors(map.valueAt(i))) {
+                    return true;
+                }
+            }
+        } else if (value instanceof Map<?, ?>) {
+            Map<?, ?> map = (Map<?, ?>) value;
+            for (Map.Entry<?, ?> entry : map.entrySet()) {
+                if (hasFileDescriptors(entry.getKey())
+                        || hasFileDescriptors(entry.getValue())) {
+                    return true;
+                }
+            }
+        } else if (value instanceof List<?>) {
+            List<?> list = (List<?>) value;
+            for (int i = 0, n = list.size(); i < n; i++) {
+                if (hasFileDescriptors(list.get(i))) {
+                    return true;
+                }
+            }
+        } else if (value instanceof SparseArray<?>) {
+            SparseArray<?> array = (SparseArray<?>) value;
+            for (int i = 0, n = array.size(); i < n; i++) {
+                if (hasFileDescriptors(array.valueAt(i))) {
+                    return true;
+                }
+            }
+        } else if (value instanceof Object[]) {
+            Object[] array = (Object[]) value;
+            for (int i = 0, n = array.length; i < n; i++) {
+                if (hasFileDescriptors(array[i])) {
+                    return true;
+                }
+            }
+        } else {
+            getValueType(value); // Will throw if value is not supported
+        }
+        return false;
+    }
+
+    /**
+     * Store or read an IBinder interface token in the parcel at the current
+     * {@link #dataPosition}. This is used to validate that the marshalled
+     * transaction is intended for the target interface. This is typically written
+     * at the beginning of transactions as a header.
+     */
+    public final void writeInterfaceToken(@NonNull String interfaceName) {
+        nativeWriteInterfaceToken(mNativePtr, interfaceName);
+    }
+
+    /**
+     * Read the header written by writeInterfaceToken and verify it matches
+     * the interface name in question. If the wrong interface type is present,
+     * {@link SecurityException} is thrown. When used over binder, this exception
+     * should propagate to the caller.
+     */
+    public final void enforceInterface(@NonNull String interfaceName) {
+        nativeEnforceInterface(mNativePtr, interfaceName);
+    }
+
+    /**
+     * Verify there are no bytes left to be read on the Parcel.
+     *
+     * @throws BadParcelableException If the current position hasn't reached the end of the Parcel.
+     * When used over binder, this exception should propagate to the caller.
+     */
+    public void enforceNoDataAvail() {
+        final int n = dataAvail();
+        if (n > 0) {
+            throw new BadParcelableException("Parcel data not fully consumed, unread size: " + n);
+        }
+    }
+
+    /**
+     * Writes the work source uid to the request headers.
+     *
+     * <p>It requires the headers to have been written/read already to replace the work source.
+     *
+     * @return true if the request headers have been updated.
+     *
+     * @hide
+     */
+    public boolean replaceCallingWorkSourceUid(int workSourceUid) {
+        return nativeReplaceCallingWorkSourceUid(mNativePtr, workSourceUid);
+    }
+
+    /**
+     * Reads the work source uid from the request headers.
+     *
+     * <p>Unlike other read methods, this method does not read the parcel at the current
+     * {@link #dataPosition}. It will set the {@link #dataPosition} before the read and restore the
+     * position after reading the request header.
+     *
+     * @return the work source uid or {@link Binder#UNSET_WORKSOURCE} if headers have not been
+     * written/parsed yet.
+     *
+     * @hide
+     */
+    public int readCallingWorkSourceUid() {
+        return nativeReadCallingWorkSourceUid(mNativePtr);
+    }
+
+    /**
+     * Write a byte array into the parcel at the current {@link #dataPosition},
+     * growing {@link #dataCapacity} if needed.
+     * @param b Bytes to place into the parcel.
+     */
+    public final void writeByteArray(@Nullable byte[] b) {
+        writeByteArray(b, 0, (b != null) ? b.length : 0);
+    }
+
+    /**
+     * Write a byte array into the parcel at the current {@link #dataPosition},
+     * growing {@link #dataCapacity} if needed.
+     * @param b Bytes to place into the parcel.
+     * @param offset Index of first byte to be written.
+     * @param len Number of bytes to write.
+     */
+    public final void writeByteArray(@Nullable byte[] b, int offset, int len) {
+        if (b == null) {
+            writeInt(-1);
+            return;
+        }
+        ArrayUtils.throwsIfOutOfBounds(b.length, offset, len);
+        nativeWriteByteArray(mNativePtr, b, offset, len);
+    }
+
+    /**
+     * Write a blob of data into the parcel at the current {@link #dataPosition},
+     * growing {@link #dataCapacity} if needed.
+     *
+     * <p> If the blob is small, then it is stored in-place, otherwise it is transferred by way of
+     * an anonymous shared memory region. If you prefer send in-place, please use
+     * {@link #writeByteArray(byte[])}.
+     *
+     * @param b Bytes to place into the parcel.
+     *
+     * @see #readBlob()
+     */
+    public final void writeBlob(@Nullable byte[] b) {
+        writeBlob(b, 0, (b != null) ? b.length : 0);
+    }
+
+    /**
+     * Write a blob of data into the parcel at the current {@link #dataPosition},
+     * growing {@link #dataCapacity} if needed.
+     *
+     * <p> If the blob is small, then it is stored in-place, otherwise it is transferred by way of
+     * an anonymous shared memory region. If you prefer send in-place, please use
+     * {@link #writeByteArray(byte[], int, int)}.
+     *
+     * @param b Bytes to place into the parcel.
+     * @param offset Index of first byte to be written.
+     * @param len Number of bytes to write.
+     *
+     * @see #readBlob()
+     */
+    public final void writeBlob(@Nullable byte[] b, int offset, int len) {
+        if (b == null) {
+            writeInt(-1);
+            return;
+        }
+        ArrayUtils.throwsIfOutOfBounds(b.length, offset, len);
+        nativeWriteBlob(mNativePtr, b, offset, len);
+    }
+
+    // The OK status from system/core/libutils/include/utils/Errors.h .
+    // We shall pass all other error codes back to native for throwing exceptions. The error
+    // check is done in Java to allow using @CriticalNative calls for the success path.
+    private static final int OK = 0;
+
+    /**
+     * Write an integer value into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeInt(int val) {
+        int err = nativeWriteInt(mNativePtr, val);
+        if (err != OK) {
+            nativeSignalExceptionForError(err);
+        }
+    }
+
+    /**
+     * Write a long integer value into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeLong(long val) {
+        int err = nativeWriteLong(mNativePtr, val);
+        if (err != OK) {
+            nativeSignalExceptionForError(err);
+        }
+    }
+
+    /**
+     * Write a floating point value into the parcel at the current
+     * dataPosition(), growing dataCapacity() if needed.
+     */
+    public final void writeFloat(float val) {
+        int err = nativeWriteFloat(mNativePtr, val);
+        if (err != OK) {
+            nativeSignalExceptionForError(err);
+        }
+    }
+
+    /**
+     * Write a double precision floating point value into the parcel at the
+     * current dataPosition(), growing dataCapacity() if needed.
+     */
+    public final void writeDouble(double val) {
+        int err = nativeWriteDouble(mNativePtr, val);
+        if (err != OK) {
+            nativeSignalExceptionForError(err);
+        }
+    }
+
+    /**
+     * Write a string value into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeString(@Nullable String val) {
+        writeString16(val);
+    }
+
+    /** {@hide} */
+    public final void writeString8(@Nullable String val) {
+        mReadWriteHelper.writeString8(this, val);
+    }
+
+    /** {@hide} */
+    public final void writeString16(@Nullable String val) {
+        mReadWriteHelper.writeString16(this, val);
+    }
+
+    /**
+     * Write a string without going though a {@link ReadWriteHelper}.  Subclasses of
+     * {@link ReadWriteHelper} must use this method instead of {@link #writeString} to avoid
+     * infinity recursive calls.
+     *
+     * @hide
+     */
+    public void writeStringNoHelper(@Nullable String val) {
+        writeString16NoHelper(val);
+    }
+
+    /** {@hide} */
+    public void writeString8NoHelper(@Nullable String val) {
+        nativeWriteString8(mNativePtr, val);
+    }
+
+    /** {@hide} */
+    public void writeString16NoHelper(@Nullable String val) {
+        nativeWriteString16(mNativePtr, val);
+    }
+
+    /**
+     * Write a boolean value into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     *
+     * <p>Note: This method currently delegates to writeInt with a value of 1 or 0
+     * for true or false, respectively, but may change in the future.
+     */
+    public final void writeBoolean(boolean val) {
+        writeInt(val ? 1 : 0);
+    }
+
+    /**
+     * Write a CharSequence value into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public final void writeCharSequence(@Nullable CharSequence val) {
+        TextUtils.writeToParcel(val, this, 0);
+    }
+
+    /**
+     * Write an object into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeStrongBinder(IBinder val) {
+        nativeWriteStrongBinder(mNativePtr, val);
+    }
+
+    /**
+     * Write an object into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeStrongInterface(IInterface val) {
+        writeStrongBinder(val == null ? null : val.asBinder());
+    }
+
+    /**
+     * Write a FileDescriptor into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     *
+     * <p class="caution">The file descriptor will not be closed, which may
+     * result in file descriptor leaks when objects are returned from Binder
+     * calls.  Use {@link ParcelFileDescriptor#writeToParcel} instead, which
+     * accepts contextual flags and will close the original file descriptor
+     * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
+     */
+    public final void writeFileDescriptor(@NonNull FileDescriptor val) {
+        nativeWriteFileDescriptor(mNativePtr, val);
+    }
+
+    /**
+     * {@hide}
+     * This will be the new name for writeFileDescriptor, for consistency.
+     **/
+    public final void writeRawFileDescriptor(@NonNull FileDescriptor val) {
+        nativeWriteFileDescriptor(mNativePtr, val);
+    }
+
+    /**
+     * {@hide}
+     * Write an array of FileDescriptor objects into the Parcel.
+     *
+     * @param value The array of objects to be written.
+     */
+    public final void writeRawFileDescriptorArray(@Nullable FileDescriptor[] value) {
+        if (value != null) {
+            int N = value.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeRawFileDescriptor(value[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * Write a byte value into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     *
+     * <p>Note: This method currently delegates to writeInt but may change in
+     * the future.
+     */
+    public final void writeByte(byte val) {
+        writeInt(val);
+    }
+
+    /**
+     * Please use {@link #writeBundle} instead.  Flattens a Map into the parcel
+     * at the current dataPosition(),
+     * growing dataCapacity() if needed.  The Map keys must be String objects.
+     * The Map values are written using {@link #writeValue} and must follow
+     * the specification there.
+     *
+     * <p>It is strongly recommended to use {@link #writeBundle} instead of
+     * this method, since the Bundle class provides a type-safe API that
+     * allows you to avoid mysterious type errors at the point of marshalling.
+     */
+    public final void writeMap(@Nullable Map val) {
+        writeMapInternal((Map<String, Object>) val);
+    }
+
+    /**
+     * Flatten a Map into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.  The Map keys must be String objects.
+     */
+    /* package */ void writeMapInternal(@Nullable Map<String,Object> val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        Set<Map.Entry<String,Object>> entries = val.entrySet();
+        int size = entries.size();
+        writeInt(size);
+
+        for (Map.Entry<String,Object> e : entries) {
+            writeValue(e.getKey());
+            writeValue(e.getValue());
+            size--;
+        }
+
+        if (size != 0) {
+            throw new BadParcelableException("Map size does not match number of entries!");
+        }
+
+    }
+
+    /**
+     * Flatten an ArrayMap into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.  The Map keys must be String objects.
+     */
+    /* package */ void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        // Keep the format of this Parcel in sync with writeToParcelInner() in
+        // frameworks/native/libs/binder/PersistableBundle.cpp.
+        final int N = val.size();
+        writeInt(N);
+        if (DEBUG_ARRAY_MAP) {
+            RuntimeException here =  new RuntimeException("here");
+            here.fillInStackTrace();
+            Log.d(TAG, "Writing " + N + " ArrayMap entries", here);
+        }
+        int startPos;
+        for (int i=0; i<N; i++) {
+            if (DEBUG_ARRAY_MAP) startPos = dataPosition();
+            writeString(val.keyAt(i));
+            writeValue(val.valueAt(i));
+            if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Write #" + i + " "
+                    + (dataPosition()-startPos) + " bytes: key=0x"
+                    + Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)
+                    + " " + val.keyAt(i));
+        }
+    }
+
+    /**
+     * @hide For testing only.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void writeArrayMap(@Nullable ArrayMap<String, Object> val) {
+        writeArrayMapInternal(val);
+    }
+
+    /**
+     * Flatten an {@link ArrayMap} with string keys containing a particular object
+     * type into the parcel at the current dataPosition() and growing dataCapacity()
+     * if needed. The type of the objects in the array must be one that implements
+     * Parcelable. Only the raw data of the objects is written and not their type,
+     * so you must use the corresponding {@link #createTypedArrayMap(Parcelable.Creator)}
+     *
+     * @param val The map of objects to be written.
+     * @param parcelableFlags The parcelable flags to use.
+     *
+     * @see #createTypedArrayMap(Parcelable.Creator)
+     * @see Parcelable
+     */
+    public <T extends Parcelable> void writeTypedArrayMap(@Nullable ArrayMap<String, T> val,
+            int parcelableFlags) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        final int count = val.size();
+        writeInt(count);
+        for (int i = 0; i < count; i++) {
+            writeString(val.keyAt(i));
+            writeTypedObject(val.valueAt(i), parcelableFlags);
+        }
+    }
+
+    /**
+     * Write an array set to the parcel.
+     *
+     * @param val The array set to write.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void writeArraySet(@Nullable ArraySet<? extends Object> val) {
+        final int size = (val != null) ? val.size() : -1;
+        writeInt(size);
+        for (int i = 0; i < size; i++) {
+            writeValue(val.valueAt(i));
+        }
+    }
+
+    /**
+     * Flatten a Bundle into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeBundle(@Nullable Bundle val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+
+        val.writeToParcel(this, 0);
+    }
+
+    /**
+     * Flatten a PersistableBundle into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writePersistableBundle(@Nullable PersistableBundle val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+
+        val.writeToParcel(this, 0);
+    }
+
+    /**
+     * Flatten a Size into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeSize(@NonNull Size val) {
+        writeInt(val.getWidth());
+        writeInt(val.getHeight());
+    }
+
+    /**
+     * Flatten a SizeF into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writeSizeF(@NonNull SizeF val) {
+        writeFloat(val.getWidth());
+        writeFloat(val.getHeight());
+    }
+
+    /**
+     * Flatten a List into the parcel at the current dataPosition(), growing
+     * dataCapacity() if needed.  The List values are written using
+     * {@link #writeValue} and must follow the specification there.
+     */
+    public final void writeList(@Nullable List val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        int i=0;
+        writeInt(N);
+        while (i < N) {
+            writeValue(val.get(i));
+            i++;
+        }
+    }
+
+    /**
+     * Flatten an Object array into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.  The array values are written using
+     * {@link #writeValue} and must follow the specification there.
+     */
+    public final void writeArray(@Nullable Object[] val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.length;
+        int i=0;
+        writeInt(N);
+        while (i < N) {
+            writeValue(val[i]);
+            i++;
+        }
+    }
+
+    /**
+     * Flatten a generic SparseArray into the parcel at the current
+     * dataPosition(), growing dataCapacity() if needed.  The SparseArray
+     * values are written using {@link #writeValue} and must follow the
+     * specification there.
+     */
+    public final <T> void writeSparseArray(@Nullable SparseArray<T> val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        writeInt(N);
+        int i=0;
+        while (i < N) {
+            writeInt(val.keyAt(i));
+            writeValue(val.valueAt(i));
+            i++;
+        }
+    }
+
+    public final void writeSparseBooleanArray(@Nullable SparseBooleanArray val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        writeInt(N);
+        int i=0;
+        while (i < N) {
+            writeInt(val.keyAt(i));
+            writeByte((byte)(val.valueAt(i) ? 1 : 0));
+            i++;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public final void writeSparseIntArray(@Nullable SparseIntArray val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        writeInt(N);
+        int i=0;
+        while (i < N) {
+            writeInt(val.keyAt(i));
+            writeInt(val.valueAt(i));
+            i++;
+        }
+    }
+
+    public final void writeBooleanArray(@Nullable boolean[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeInt(val[i] ? 1 : 0);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    @Nullable
+    public final boolean[] createBooleanArray() {
+        int N = readInt();
+        // >>2 as a fast divide-by-4 works in the create*Array() functions
+        // because dataAvail() will never return a negative number.  4 is
+        // the size of a stored boolean in the stream.
+        if (N >= 0 && N <= (dataAvail() >> 2)) {
+            boolean[] val = new boolean[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readInt() != 0;
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    public final void readBooleanArray(@NonNull boolean[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readInt() != 0;
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    /** @hide */
+    public void writeShortArray(@Nullable short[] val) {
+        if (val != null) {
+            int n = val.length;
+            writeInt(n);
+            for (int i = 0; i < n; i++) {
+                writeInt(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /** @hide */
+    @Nullable
+    public short[] createShortArray() {
+        int n = readInt();
+        if (n >= 0 && n <= (dataAvail() >> 2)) {
+            short[] val = new short[n];
+            for (int i = 0; i < n; i++) {
+                val[i] = (short) readInt();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public void readShortArray(@NonNull short[] val) {
+        int n = readInt();
+        if (n == val.length) {
+            for (int i = 0; i < n; i++) {
+                val[i] = (short) readInt();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    public final void writeCharArray(@Nullable char[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeInt((int)val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    @Nullable
+    public final char[] createCharArray() {
+        int N = readInt();
+        if (N >= 0 && N <= (dataAvail() >> 2)) {
+            char[] val = new char[N];
+            for (int i=0; i<N; i++) {
+                val[i] = (char)readInt();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    public final void readCharArray(@NonNull char[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = (char)readInt();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    public final void writeIntArray(@Nullable int[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeInt(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    @Nullable
+    public final int[] createIntArray() {
+        int N = readInt();
+        if (N >= 0 && N <= (dataAvail() >> 2)) {
+            int[] val = new int[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readInt();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    public final void readIntArray(@NonNull int[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readInt();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    public final void writeLongArray(@Nullable long[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeLong(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    @Nullable
+    public final long[] createLongArray() {
+        int N = readInt();
+        // >>3 because stored longs are 64 bits
+        if (N >= 0 && N <= (dataAvail() >> 3)) {
+            long[] val = new long[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readLong();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    public final void readLongArray(@NonNull long[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readLong();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    public final void writeFloatArray(@Nullable float[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeFloat(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    @Nullable
+    public final float[] createFloatArray() {
+        int N = readInt();
+        // >>2 because stored floats are 4 bytes
+        if (N >= 0 && N <= (dataAvail() >> 2)) {
+            float[] val = new float[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readFloat();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    public final void readFloatArray(@NonNull float[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readFloat();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    public final void writeDoubleArray(@Nullable double[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeDouble(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    @Nullable
+    public final double[] createDoubleArray() {
+        int N = readInt();
+        // >>3 because stored doubles are 8 bytes
+        if (N >= 0 && N <= (dataAvail() >> 3)) {
+            double[] val = new double[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readDouble();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    public final void readDoubleArray(@NonNull double[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readDouble();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    public final void writeStringArray(@Nullable String[] val) {
+        writeString16Array(val);
+    }
+
+    @Nullable
+    public final String[] createStringArray() {
+        return createString16Array();
+    }
+
+    public final void readStringArray(@NonNull String[] val) {
+        readString16Array(val);
+    }
+
+    /** {@hide} */
+    public final void writeString8Array(@Nullable String[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeString8(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /** {@hide} */
+    @Nullable
+    public final String[] createString8Array() {
+        int N = readInt();
+        if (N >= 0) {
+            String[] val = new String[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readString8();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    public final void readString8Array(@NonNull String[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readString8();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    /** {@hide} */
+    public final void writeString16Array(@Nullable String[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeString16(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /** {@hide} */
+    @Nullable
+    public final String[] createString16Array() {
+        int N = readInt();
+        if (N >= 0) {
+            String[] val = new String[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readString16();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    public final void readString16Array(@NonNull String[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readString16();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    public final void writeBinderArray(@Nullable IBinder[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeStrongBinder(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * Flatten a homogeneous array containing an IInterface type into the parcel,
+     * at the current dataPosition() and growing dataCapacity() if needed.  The
+     * type of the objects in the array must be one that implements IInterface.
+     *
+     * @param val The array of objects to be written.
+     *
+     * @see #createInterfaceArray
+     * @see #readInterfaceArray
+     * @see IInterface
+     */
+    public final <T extends IInterface> void writeInterfaceArray(
+            @SuppressLint("ArrayReturn") @Nullable T[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeStrongInterface(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public final void writeCharSequenceArray(@Nullable CharSequence[] val) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeCharSequence(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public final void writeCharSequenceList(@Nullable ArrayList<CharSequence> val) {
+        if (val != null) {
+            int N = val.size();
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeCharSequence(val.get(i));
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    @Nullable
+    public final IBinder[] createBinderArray() {
+        int N = readInt();
+        if (N >= 0) {
+            IBinder[] val = new IBinder[N];
+            for (int i=0; i<N; i++) {
+                val[i] = readStrongBinder();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    public final void readBinderArray(@NonNull IBinder[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readStrongBinder();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    /**
+     * Read and return a new array of T (IInterface) from the parcel.
+     *
+     * @return the IInterface array of type T
+     * @param newArray a function to create an array of T with a given length
+     * @param asInterface a function to convert IBinder object into T (IInterface)
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection", "SamShouldBeLast"})
+    @Nullable
+    public final <T extends IInterface> T[] createInterfaceArray(
+            @NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
+        int N = readInt();
+        if (N >= 0) {
+            T[] val = newArray.apply(N);
+            for (int i=0; i<N; i++) {
+                val[i] = asInterface.apply(readStrongBinder());
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Read an array of T (IInterface) from a parcel.
+     *
+     * @param asInterface a function to convert IBinder object into T (IInterface)
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the length of `val`
+     *    mismatches the number of items in the parcel.
+     */
+    public final <T extends IInterface> void readInterfaceArray(
+            @SuppressLint("ArrayReturn") @NonNull T[] val,
+            @NonNull Function<IBinder, T> asInterface) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = asInterface.apply(readStrongBinder());
+            }
+        } else {
+            throw new BadParcelableException("bad array lengths");
+        }
+    }
+
+    /**
+     * Flatten a List containing a particular object type into the parcel, at
+     * the current dataPosition() and growing dataCapacity() if needed.  The
+     * type of the objects in the list must be one that implements Parcelable.
+     * Unlike the generic writeList() method, however, only the raw data of the
+     * objects is written and not their type, so you must use the corresponding
+     * readTypedList() to unmarshall them.
+     *
+     * @param val The list of objects to be written.
+     *
+     * @see #createTypedArrayList
+     * @see #readTypedList
+     * @see Parcelable
+     */
+    public final <T extends Parcelable> void writeTypedList(@Nullable List<T> val) {
+        writeTypedList(val, 0);
+    }
+
+    /**
+     * Flatten a {@link SparseArray} containing a particular object type into the parcel
+     * at the current dataPosition() and growing dataCapacity() if needed. The
+     * type of the objects in the array must be one that implements Parcelable.
+     * Unlike the generic {@link #writeSparseArray(SparseArray)} method, however, only
+     * the raw data of the objects is written and not their type, so you must use the
+     * corresponding {@link #createTypedSparseArray(Parcelable.Creator)}.
+     *
+     * @param val The list of objects to be written.
+     * @param parcelableFlags The parcelable flags to use.
+     *
+     * @see #createTypedSparseArray(Parcelable.Creator)
+     * @see Parcelable
+     */
+    public final <T extends Parcelable> void writeTypedSparseArray(@Nullable SparseArray<T> val,
+            int parcelableFlags) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        final int count = val.size();
+        writeInt(count);
+        for (int i = 0; i < count; i++) {
+            writeInt(val.keyAt(i));
+            writeTypedObject(val.valueAt(i), parcelableFlags);
+        }
+    }
+
+    /**
+     * Flatten a List containing a particular object type into the parcel, at
+     * the current dataPosition() and growing dataCapacity() if needed.  The
+     * type of the objects in the list must be one that implements Parcelable.
+     * Unlike the generic writeList() method, however, only the raw data of the
+     * objects is written and not their type, so you must use the corresponding
+     * readTypedList() to unmarshall them.
+     *
+     * @param val The list of objects to be written.
+     * @param parcelableFlags Contextual flags as per
+     * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     *
+     * @see #createTypedArrayList
+     * @see #readTypedList
+     * @see Parcelable
+     */
+    public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        int i=0;
+        writeInt(N);
+        while (i < N) {
+            writeTypedObject(val.get(i), parcelableFlags);
+            i++;
+        }
+    }
+
+    /**
+     * Flatten a List containing String objects into the parcel, at
+     * the current dataPosition() and growing dataCapacity() if needed.  They
+     * can later be retrieved with {@link #createStringArrayList} or
+     * {@link #readStringList}.
+     *
+     * @param val The list of strings to be written.
+     *
+     * @see #createStringArrayList
+     * @see #readStringList
+     */
+    public final void writeStringList(@Nullable List<String> val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        int i=0;
+        writeInt(N);
+        while (i < N) {
+            writeString(val.get(i));
+            i++;
+        }
+    }
+
+    /**
+     * Flatten a List containing IBinder objects into the parcel, at
+     * the current dataPosition() and growing dataCapacity() if needed.  They
+     * can later be retrieved with {@link #createBinderArrayList} or
+     * {@link #readBinderList}.
+     *
+     * @param val The list of strings to be written.
+     *
+     * @see #createBinderArrayList
+     * @see #readBinderList
+     */
+    public final void writeBinderList(@Nullable List<IBinder> val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        int i=0;
+        writeInt(N);
+        while (i < N) {
+            writeStrongBinder(val.get(i));
+            i++;
+        }
+    }
+
+    /**
+     * Flatten a {@code List} containing T (IInterface) objects into this parcel
+     * at the current position. They can later be retrieved with
+     * {@link #createInterfaceArrayList} or {@link #readInterfaceList}.
+     *
+     * @see #createInterfaceArrayList
+     * @see #readInterfaceList
+     */
+    public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        int i=0;
+        writeInt(N);
+        while (i < N) {
+            writeStrongInterface(val.get(i));
+            i++;
+        }
+    }
+
+    /**
+     * Flatten a {@code List} containing arbitrary {@code Parcelable} objects into this parcel
+     * at the current position. They can later be retrieved using
+     * {@link #readParcelableList(List, ClassLoader)} if required.
+     *
+     * @see #readParcelableList(List, ClassLoader)
+     */
+    public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+
+        int N = val.size();
+        int i=0;
+        writeInt(N);
+        while (i < N) {
+            writeParcelable(val.get(i), flags);
+            i++;
+        }
+    }
+
+    /**
+     * Flatten a homogeneous array containing a particular object type into
+     * the parcel, at
+     * the current dataPosition() and growing dataCapacity() if needed.  The
+     * type of the objects in the array must be one that implements Parcelable.
+     * Unlike the {@link #writeParcelableArray} method, however, only the
+     * raw data of the objects is written and not their type, so you must use
+     * {@link #readTypedArray} with the correct corresponding
+     * {@link Parcelable.Creator} implementation to unmarshall them.
+     *
+     * @param val The array of objects to be written.
+     * @param parcelableFlags Contextual flags as per
+     * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     *
+     * @see #readTypedArray
+     * @see #writeParcelableArray
+     * @see Parcelable.Creator
+     */
+    public final <T extends Parcelable> void writeTypedArray(@Nullable T[] val,
+            int parcelableFlags) {
+        if (val != null) {
+            int N = val.length;
+            writeInt(N);
+            for (int i = 0; i < N; i++) {
+                writeTypedObject(val[i], parcelableFlags);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * Flatten the Parcelable object into the parcel.
+     *
+     * @param val The Parcelable object to be written.
+     * @param parcelableFlags Contextual flags as per
+     * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     *
+     * @see #readTypedObject
+     */
+    public final <T extends Parcelable> void writeTypedObject(@Nullable T val,
+            int parcelableFlags) {
+        if (val != null) {
+            writeInt(1);
+            val.writeToParcel(this, parcelableFlags);
+        } else {
+            writeInt(0);
+        }
+    }
+
+    /**
+     * Flatten a homogeneous multi-dimensional array with fixed-size.  This delegates to other
+     * APIs to write a one-dimensional array.  Use {@link #readFixedArray(Object)} or
+     * {@link #createFixedArray(Class, int[])} with the same dimensions to unmarshal.
+     *
+     * @param val The array to be written.
+     * @param parcelableFlags Contextual flags as per
+     *   {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     *   Used only if val is an array of Parcelable objects.
+     * @param dimensions an array of int representing length of each dimension. The array should be
+     *   sized with the exact size of dimensions.
+     *
+     * @see #readFixedArray
+     * @see #createFixedArray createFixedArray(Class&lt;T&gt;, Parcelable.Creator&lt;S&gt;, int...)
+     * @see #writeBooleanArray
+     * @see #writeByteArray
+     * @see #writeCharArray
+     * @see #writeIntArray
+     * @see #writeLongArray
+     * @see #writeFloatArray
+     * @see #writeDoubleArray
+     * @see #writeBinderArray
+     * @see #writeInterfaceArray
+     * @see #writeTypedArray
+     * @throws BadParcelableException If the array's component type is not supported or if its
+     *   size doesn't match with the given dimensions.
+     */
+    public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
+            @NonNull int... dimensions) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        writeFixedArrayInternal(val, parcelableFlags, /*index=*/0, dimensions);
+    }
+
+    private <T> void writeFixedArrayInternal(T val, int parcelableFlags, int index,
+            int[] dimensions) {
+        if (index >= dimensions.length) {
+            throw new BadParcelableException("Array has more dimensions than expected: "
+                + dimensions.length);
+        }
+
+        int length = dimensions[index];
+
+        // val should be an array of length N
+        if (val == null) {
+            throw new BadParcelableException("Non-null array shouldn't have a null array.");
+        }
+        if (!val.getClass().isArray()) {
+            throw new BadParcelableException("Not an array: " + val);
+        }
+        if (Array.getLength(val) != length) {
+            throw new BadParcelableException("bad length: expected " + length + ", but got "
+                + Array.getLength(val));
+        }
+
+        // Delegates to other writers if this is a one-dimensional array.
+        // Otherwise, write component arrays with recursive calls.
+
+        final Class<?> componentType = val.getClass().getComponentType();
+        if (!componentType.isArray() && index + 1 != dimensions.length) {
+            throw new BadParcelableException("Array has fewer dimensions than expected: "
+                + dimensions.length);
+        }
+        if (componentType == boolean.class) {
+            writeBooleanArray((boolean[]) val);
+        } else if (componentType == byte.class) {
+            writeByteArray((byte[]) val);
+        } else if (componentType == char.class) {
+            writeCharArray((char[]) val);
+        } else if (componentType == int.class) {
+            writeIntArray((int[]) val);
+        } else if (componentType == long.class) {
+            writeLongArray((long[]) val);
+        } else if (componentType == float.class) {
+            writeFloatArray((float[]) val);
+        } else if (componentType == double.class) {
+            writeDoubleArray((double[]) val);
+        } else if (componentType == IBinder.class) {
+            writeBinderArray((IBinder[]) val);
+        } else if (IInterface.class.isAssignableFrom(componentType)) {
+            writeInterfaceArray((IInterface[]) val);
+        } else if (Parcelable.class.isAssignableFrom(componentType)) {
+            writeTypedArray((Parcelable[]) val, parcelableFlags);
+        } else if (componentType.isArray()) {
+            writeInt(length);
+            for (int i = 0; i < length; i++) {
+                writeFixedArrayInternal(Array.get(val, i), parcelableFlags, index + 1,
+                        dimensions);
+            }
+        } else {
+            throw new BadParcelableException("unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    /**
+     * Flatten a generic object in to a parcel.  The given Object value may
+     * currently be one of the following types:
+     *
+     * <ul>
+     * <li> null
+     * <li> String
+     * <li> Byte
+     * <li> Short
+     * <li> Integer
+     * <li> Long
+     * <li> Float
+     * <li> Double
+     * <li> Boolean
+     * <li> String[]
+     * <li> boolean[]
+     * <li> byte[]
+     * <li> int[]
+     * <li> long[]
+     * <li> Object[] (supporting objects of the same type defined here).
+     * <li> {@link Bundle}
+     * <li> Map (as supported by {@link #writeMap}).
+     * <li> Any object that implements the {@link Parcelable} protocol.
+     * <li> Parcelable[]
+     * <li> CharSequence (as supported by {@link TextUtils#writeToParcel}).
+     * <li> List (as supported by {@link #writeList}).
+     * <li> {@link SparseArray} (as supported by {@link #writeSparseArray(SparseArray)}).
+     * <li> {@link IBinder}
+     * <li> Any object that implements Serializable (but see
+     *      {@link #writeSerializable} for caveats).  Note that all of the
+     *      previous types have relatively efficient implementations for
+     *      writing to a Parcel; having to rely on the generic serialization
+     *      approach is much less efficient and should be avoided whenever
+     *      possible.
+     * </ul>
+     *
+     * <p class="caution">{@link Parcelable} objects are written with
+     * {@link Parcelable#writeToParcel} using contextual flags of 0.  When
+     * serializing objects containing {@link ParcelFileDescriptor}s,
+     * this may result in file descriptor leaks when they are returned from
+     * Binder calls (where {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}
+     * should be used).</p>
+     */
+    public final void writeValue(@Nullable Object v) {
+        if (v instanceof LazyValue) {
+            LazyValue value = (LazyValue) v;
+            value.writeToParcel(this);
+            return;
+        }
+        int type = getValueType(v);
+        writeInt(type);
+        if (isLengthPrefixed(type)) {
+            // Length
+            int length = dataPosition();
+            writeInt(-1); // Placeholder
+            // Object
+            int start = dataPosition();
+            writeValue(type, v);
+            int end = dataPosition();
+            // Backpatch length
+            setDataPosition(length);
+            writeInt(end - start);
+            setDataPosition(end);
+        } else {
+            writeValue(type, v);
+        }
+    }
+
+    /** @hide */
+    public static int getValueType(@Nullable Object v) {
+        if (v == null) {
+            return VAL_NULL;
+        } else if (v instanceof String) {
+            return VAL_STRING;
+        } else if (v instanceof Integer) {
+            return VAL_INTEGER;
+        } else if (v instanceof Map) {
+            return VAL_MAP;
+        } else if (v instanceof Bundle) {
+            // Must be before Parcelable
+            return VAL_BUNDLE;
+        } else if (v instanceof PersistableBundle) {
+            // Must be before Parcelable
+            return VAL_PERSISTABLEBUNDLE;
+        } else if (v instanceof SizeF) {
+            // Must be before Parcelable
+            return VAL_SIZEF;
+        } else if (v instanceof Parcelable) {
+            // IMPOTANT: cases for classes that implement Parcelable must
+            // come before the Parcelable case, so that their speci fic VAL_*
+            // types will be written.
+            return VAL_PARCELABLE;
+        } else if (v instanceof Short) {
+            return VAL_SHORT;
+        } else if (v instanceof Long) {
+            return VAL_LONG;
+        } else if (v instanceof Float) {
+            return VAL_FLOAT;
+        } else if (v instanceof Double) {
+            return VAL_DOUBLE;
+        } else if (v instanceof Boolean) {
+            return VAL_BOOLEAN;
+        } else if (v instanceof CharSequence) {
+            // Must be after String
+            return VAL_CHARSEQUENCE;
+        } else if (v instanceof List) {
+            return VAL_LIST;
+        } else if (v instanceof SparseArray) {
+            return VAL_SPARSEARRAY;
+        } else if (v instanceof boolean[]) {
+            return VAL_BOOLEANARRAY;
+        } else if (v instanceof byte[]) {
+            return VAL_BYTEARRAY;
+        } else if (v instanceof String[]) {
+            return VAL_STRINGARRAY;
+        } else if (v instanceof CharSequence[]) {
+            // Must be after String[] and before Object[]
+            return VAL_CHARSEQUENCEARRAY;
+        } else if (v instanceof IBinder) {
+            return VAL_IBINDER;
+        } else if (v instanceof Parcelable[]) {
+            return VAL_PARCELABLEARRAY;
+        } else if (v instanceof int[]) {
+            return VAL_INTARRAY;
+        } else if (v instanceof long[]) {
+            return VAL_LONGARRAY;
+        } else if (v instanceof Byte) {
+            return VAL_BYTE;
+        } else if (v instanceof Size) {
+            return VAL_SIZE;
+        } else if (v instanceof double[]) {
+            return VAL_DOUBLEARRAY;
+        } else if (v instanceof Character) {
+            return VAL_CHAR;
+        } else if (v instanceof short[]) {
+            return VAL_SHORTARRAY;
+        } else if (v instanceof char[]) {
+            return VAL_CHARARRAY;
+        } else  if (v instanceof float[]) {
+            return VAL_FLOATARRAY;
+        } else {
+            Class<?> clazz = v.getClass();
+            if (clazz.isArray() && clazz.getComponentType() == Object.class) {
+                // Only pure Object[] are written here, Other arrays of non-primitive types are
+                // handled by serialization as this does not record the component type.
+                return VAL_OBJECTARRAY;
+            } else if (v instanceof Serializable) {
+                // Must be last
+                return VAL_SERIALIZABLE;
+            } else {
+                throw new IllegalArgumentException("Parcel: unknown type for value " + v);
+            }
+        }
+    }
+    /**
+     * Writes value {@code v} in the parcel. This does NOT write the int representing the type
+     * first.
+     *
+     * @hide
+     */
+    public void writeValue(int type, @Nullable Object v) {
+        switch (type) {
+            case VAL_NULL:
+                break;
+            case VAL_STRING:
+                writeString((String) v);
+                break;
+            case VAL_INTEGER:
+                writeInt((Integer) v);
+                break;
+            case VAL_MAP:
+                writeMap((Map) v);
+                break;
+            case VAL_BUNDLE:
+                writeBundle((Bundle) v);
+                break;
+            case VAL_PERSISTABLEBUNDLE:
+                writePersistableBundle((PersistableBundle) v);
+                break;
+            case VAL_PARCELABLE:
+                writeParcelable((Parcelable) v, 0);
+                break;
+            case VAL_SHORT:
+                writeInt(((Short) v).intValue());
+                break;
+            case VAL_LONG:
+                writeLong((Long) v);
+                break;
+            case VAL_FLOAT:
+                writeFloat((Float) v);
+                break;
+            case VAL_DOUBLE:
+                writeDouble((Double) v);
+                break;
+            case VAL_BOOLEAN:
+                writeInt((Boolean) v ? 1 : 0);
+                break;
+            case VAL_CHARSEQUENCE:
+                writeCharSequence((CharSequence) v);
+                break;
+            case VAL_LIST:
+                writeList((List) v);
+                break;
+            case VAL_SPARSEARRAY:
+                writeSparseArray((SparseArray) v);
+                break;
+            case VAL_BOOLEANARRAY:
+                writeBooleanArray((boolean[]) v);
+                break;
+            case VAL_BYTEARRAY:
+                writeByteArray((byte[]) v);
+                break;
+            case VAL_STRINGARRAY:
+                writeStringArray((String[]) v);
+                break;
+            case VAL_CHARSEQUENCEARRAY:
+                writeCharSequenceArray((CharSequence[]) v);
+                break;
+            case VAL_IBINDER:
+                writeStrongBinder((IBinder) v);
+                break;
+            case VAL_PARCELABLEARRAY:
+                writeParcelableArray((Parcelable[]) v, 0);
+                break;
+            case VAL_INTARRAY:
+                writeIntArray((int[]) v);
+                break;
+            case VAL_LONGARRAY:
+                writeLongArray((long[]) v);
+                break;
+            case VAL_BYTE:
+                writeInt((Byte) v);
+                break;
+            case VAL_SIZE:
+                writeSize((Size) v);
+                break;
+            case VAL_SIZEF:
+                writeSizeF((SizeF) v);
+                break;
+            case VAL_DOUBLEARRAY:
+                writeDoubleArray((double[]) v);
+                break;
+            case VAL_CHAR:
+                writeInt((Character) v);
+                break;
+            case VAL_SHORTARRAY:
+                writeShortArray((short[]) v);
+                break;
+            case VAL_CHARARRAY:
+                writeCharArray((char[]) v);
+                break;
+            case VAL_FLOATARRAY:
+                writeFloatArray((float[]) v);
+                break;
+            case VAL_OBJECTARRAY:
+                writeArray((Object[]) v);
+                break;
+            case VAL_SERIALIZABLE:
+                writeSerializable((Serializable) v);
+                break;
+            default:
+                throw new RuntimeException("Parcel: unable to marshal value " + v);
+        }
+    }
+
+    /**
+     * Flatten the name of the class of the Parcelable and its contents
+     * into the parcel.
+     *
+     * @param p The Parcelable object to be written.
+     * @param parcelableFlags Contextual flags as per
+     * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     */
+    public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
+        if (p == null) {
+            writeString(null);
+            return;
+        }
+        writeParcelableCreator(p);
+        p.writeToParcel(this, parcelableFlags);
+    }
+
+    /**
+     * Flatten the name of the class of the Parcelable into this Parcel.
+     *
+     * @param p The Parcelable object to be written.
+     * @see #readParcelableCreator
+     */
+    public final void writeParcelableCreator(@NonNull Parcelable p) {
+        String name = p.getClass().getName();
+        writeString(name);
+    }
+
+    /**
+     * A map used by {@link #maybeWriteSquashed} to keep track of what parcelables have
+     * been seen, and what positions they were written. The value is the absolute position of
+     * each parcelable.
+     */
+    private ArrayMap<Parcelable, Integer> mWrittenSquashableParcelables;
+
+    private void ensureWrittenSquashableParcelables() {
+        if (mWrittenSquashableParcelables != null) {
+            return;
+        }
+        mWrittenSquashableParcelables = new ArrayMap<>();
+    }
+
+    private boolean mAllowSquashing = false;
+
+    /**
+     * Allow "squashing" writes in {@link #maybeWriteSquashed}. This allows subsequent calls to
+     * {@link #maybeWriteSquashed(Parcelable)} to "squash" the same instances into one in a Parcel.
+     *
+     * Typically, this method is called at the beginning of {@link Parcelable#writeToParcel}. The
+     * caller must retain the return value from this method and call {@link #restoreAllowSquashing}
+     * with it.
+     *
+     * See {@link #maybeWriteSquashed(Parcelable)} for the details.
+     *
+     * @see #restoreAllowSquashing(boolean)
+     * @see #maybeWriteSquashed(Parcelable)
+     * @see #readSquashed(SquashReadHelper)
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean allowSquashing() {
+        boolean previous = mAllowSquashing;
+        mAllowSquashing = true;
+        return previous;
+    }
+
+    /**
+     * @see #allowSquashing()
+     * @hide
+     */
+    @TestApi
+    public void restoreAllowSquashing(boolean previous) {
+        mAllowSquashing = previous;
+        if (!mAllowSquashing) {
+            mWrittenSquashableParcelables = null;
+        }
+    }
+
+    private void resetSqaushingState() {
+        if (mAllowSquashing) {
+            Slog.wtf(TAG, "allowSquashing wasn't restored.");
+        }
+        mWrittenSquashableParcelables = null;
+        mReadSquashableParcelables = null;
+        mAllowSquashing = false;
+    }
+
+    /**
+     * A map used by {@link #readSquashed} to cache parcelables. It's a map from
+     * an absolute position in a Parcel to the parcelable stored at the position.
+     */
+    private SparseArray<Parcelable> mReadSquashableParcelables;
+
+    private void ensureReadSquashableParcelables() {
+        if (mReadSquashableParcelables != null) {
+            return;
+        }
+        mReadSquashableParcelables = new SparseArray<>();
+    }
+
+    /**
+     * Write a parcelable with "squash" -- that is, when the same instance is written to the
+     * same Parcelable multiple times, instead of writing the entire instance multiple times,
+     * only write it once, and in subsequent writes we'll only write the offset to the original
+     * object.
+     *
+     * This approach does not work of the resulting Parcel is copied with {@link #appendFrom} with
+     * a non-zero offset, so we do not enable this behavior by default. Instead, we only enable
+     * it between {@link #allowSquashing} and {@link #restoreAllowSquashing}, in order to make sure
+     * we only do so within each "top level" Parcelable.
+     *
+     * Usage: Use this method in {@link Parcelable#writeToParcel}.
+     * If this method returns TRUE, it's a subsequent call, and the offset is already written,
+     * so the caller doesn't have to do anything. If this method returns FALSE, it's the first
+     * time for the instance to be written to this parcel. The caller has to proceed with its
+     * {@link Parcelable#writeToParcel}.
+     *
+     * (See {@code ApplicationInfo} for the example.)
+     *
+     * @param p the target Parcelable to write.
+     *
+     * @see #allowSquashing()
+     * @see #restoreAllowSquashing(boolean)
+     * @see #readSquashed(SquashReadHelper)
+     *
+     * @hide
+     */
+    public boolean maybeWriteSquashed(@NonNull Parcelable p) {
+        if (!mAllowSquashing) {
+            // Don't squash, and don't put it in the map either.
+            writeInt(0);
+            return false;
+        }
+        ensureWrittenSquashableParcelables();
+        final Integer firstPos = mWrittenSquashableParcelables.get(p);
+        if (firstPos != null) {
+            // Already written.
+            // Write the relative offset from the current position to the first position.
+            final int pos = dataPosition();
+
+            // We want the offset from the next byte of this integer, so we need to +4.
+            writeInt(pos - firstPos + 4);
+            return true;
+        }
+        // First time seen, write a marker.
+        writeInt(0);
+
+        // Remember the position.
+        final int pos = dataPosition();
+        mWrittenSquashableParcelables.put(p, pos);
+
+        // Return false and let the caller actually write the content.
+        return false;
+    }
+
+    /**
+     * Helper function that's used by {@link #readSquashed(SquashReadHelper)}
+     * @hide
+     */
+    public interface SquashReadHelper<T> {
+        /** Read and instantiate {@code T} from a Parcel. */
+        @NonNull
+        T readRawParceled(@NonNull Parcel p);
+    }
+
+    /**
+     * Read a {@link Parcelable} that's written with {@link #maybeWriteSquashed}.
+     *
+     * @param reader a callback function that instantiates an instance from a parcel.
+     * Typicallly, a lambda to the instructor that takes a {@link Parcel} is passed.
+     *
+     * @see #maybeWriteSquashed(Parcelable)
+     *
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) {
+        final int offset = readInt();
+        final int pos = dataPosition();
+
+        if (offset == 0) {
+            // First time read. Unparcel, and remember it.
+            final T p = reader.readRawParceled(this);
+            ensureReadSquashableParcelables();
+            mReadSquashableParcelables.put(pos, p);
+            return p;
+        }
+        // Subsequent read.
+        final int firstAbsolutePos = pos - offset;
+
+        final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos);
+        if (p == null) {
+            final StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < mReadSquashableParcelables.size(); i++) {
+                sb.append(mReadSquashableParcelables.keyAt(i)).append(' ');
+            }
+            Slog.wtfStack(TAG, "Map doesn't contain offset "
+                    + firstAbsolutePos
+                    + " : contains=" + sb.toString());
+        }
+        return (T) p;
+    }
+
+    /**
+     * Write a generic serializable object in to a Parcel.  It is strongly
+     * recommended that this method be avoided, since the serialization
+     * overhead is extremely large, and this approach will be much slower than
+     * using the other approaches to writing data in to a Parcel.
+     */
+    public final void writeSerializable(@Nullable Serializable s) {
+        if (s == null) {
+            writeString(null);
+            return;
+        }
+        String name = s.getClass().getName();
+        writeString(name);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(s);
+            oos.close();
+
+            writeByteArray(baos.toByteArray());
+        } catch (IOException ioe) {
+            throw new BadParcelableException("Parcelable encountered "
+                    + "IOException writing serializable object (name = "
+                    + name + ")", ioe);
+        }
+    }
+
+    /** @hide For debugging purposes */
+    public static void setStackTraceParceling(boolean enabled) {
+        sParcelExceptionStackTrace = enabled;
+    }
+
+    /**
+     * Special function for writing an exception result at the header of
+     * a parcel, to be used when returning an exception from a transaction.
+     * Note that this currently only supports a few exception types; any other
+     * exception will be re-thrown by this function as a RuntimeException
+     * (to be caught by the system's last-resort exception handling when
+     * dispatching a transaction).
+     *
+     * <p>The supported exception types are:
+     * <ul>
+     * <li>{@link BadParcelableException}
+     * <li>{@link IllegalArgumentException}
+     * <li>{@link IllegalStateException}
+     * <li>{@link NullPointerException}
+     * <li>{@link SecurityException}
+     * <li>{@link UnsupportedOperationException}
+     * <li>{@link NetworkOnMainThreadException}
+     * </ul>
+     *
+     * @param e The Exception to be written.
+     *
+     * @see #writeNoException
+     * @see #readException
+     */
+    public final void writeException(@NonNull Exception e) {
+        AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
+
+        int code = getExceptionCode(e);
+        writeInt(code);
+        StrictMode.clearGatheredViolations();
+        if (code == 0) {
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+            throw new RuntimeException(e);
+        }
+        writeString(e.getMessage());
+        final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0;
+        if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace
+                > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) {
+            sLastWriteExceptionStackTrace = timeNow;
+            writeStackTrace(e);
+        } else {
+            writeInt(0);
+        }
+        switch (code) {
+            case EX_SERVICE_SPECIFIC:
+                writeInt(((ServiceSpecificException) e).errorCode);
+                break;
+            case EX_PARCELABLE:
+                // Write parceled exception prefixed by length
+                final int sizePosition = dataPosition();
+                writeInt(0);
+                writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                final int payloadPosition = dataPosition();
+                setDataPosition(sizePosition);
+                writeInt(payloadPosition - sizePosition);
+                setDataPosition(payloadPosition);
+                break;
+        }
+    }
+
+    /** @hide */
+    public static int getExceptionCode(@NonNull Throwable e) {
+        int code = 0;
+        if (e instanceof Parcelable
+                && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
+            // We only send Parcelable exceptions that are in the
+            // BootClassLoader to ensure that the receiver can unpack them
+            code = EX_PARCELABLE;
+        } else if (e instanceof SecurityException) {
+            code = EX_SECURITY;
+        } else if (e instanceof BadParcelableException) {
+            code = EX_BAD_PARCELABLE;
+        } else if (e instanceof IllegalArgumentException) {
+            code = EX_ILLEGAL_ARGUMENT;
+        } else if (e instanceof NullPointerException) {
+            code = EX_NULL_POINTER;
+        } else if (e instanceof IllegalStateException) {
+            code = EX_ILLEGAL_STATE;
+        } else if (e instanceof NetworkOnMainThreadException) {
+            code = EX_NETWORK_MAIN_THREAD;
+        } else if (e instanceof UnsupportedOperationException) {
+            code = EX_UNSUPPORTED_OPERATION;
+        } else if (e instanceof ServiceSpecificException) {
+            code = EX_SERVICE_SPECIFIC;
+        }
+        return code;
+    }
+
+    /** @hide */
+    public void writeStackTrace(@NonNull Throwable e) {
+        final int sizePosition = dataPosition();
+        writeInt(0); // Header size will be filled in later
+        StackTraceElement[] stackTrace = e.getStackTrace();
+        final int truncatedSize = Math.min(stackTrace.length, 5);
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < truncatedSize; i++) {
+            sb.append("\tat ").append(stackTrace[i]).append('\n');
+        }
+        writeString(sb.toString());
+        final int payloadPosition = dataPosition();
+        setDataPosition(sizePosition);
+        // Write stack trace header size. Used in native side to skip the header
+        writeInt(payloadPosition - sizePosition);
+        setDataPosition(payloadPosition);
+    }
+
+    /**
+     * Special function for writing information at the front of the Parcel
+     * indicating that no exception occurred.
+     *
+     * @see #writeException
+     * @see #readException
+     */
+    public final void writeNoException() {
+        AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
+
+        // Despite the name of this function ("write no exception"),
+        // it should instead be thought of as "write the RPC response
+        // header", but because this function name is written out by
+        // the AIDL compiler, we're not going to rename it.
+        //
+        // The response header, in the non-exception case (see also
+        // writeException above, also called by the AIDL compiler), is
+        // either a 0 (the default case), or EX_HAS_STRICTMODE_REPLY_HEADER if
+        // StrictMode has gathered up violations that have occurred
+        // during a Binder call, in which case we write out the number
+        // of violations and their details, serialized, before the
+        // actual RPC respons data.  The receiving end of this is
+        // readException(), below.
+        if (StrictMode.hasGatheredViolations()) {
+            writeInt(EX_HAS_STRICTMODE_REPLY_HEADER);
+            final int sizePosition = dataPosition();
+            writeInt(0);  // total size of fat header, to be filled in later
+            StrictMode.writeGatheredViolationsToParcel(this);
+            final int payloadPosition = dataPosition();
+            setDataPosition(sizePosition);
+            writeInt(payloadPosition - sizePosition);  // header size
+            setDataPosition(payloadPosition);
+        } else {
+            writeInt(0);
+        }
+    }
+
+    /**
+     * Special function for reading an exception result from the header of
+     * a parcel, to be used after receiving the result of a transaction.  This
+     * will throw the exception for you if it had been written to the Parcel,
+     * otherwise return and let you read the normal result data from the Parcel.
+     *
+     * @see #writeException
+     * @see #writeNoException
+     */
+    public final void readException() {
+        int code = readExceptionCode();
+        if (code != 0) {
+            String msg = readString();
+            readException(code, msg);
+        }
+    }
+
+    /**
+     * Parses the header of a Binder call's response Parcel and
+     * returns the exception code.  Deals with lite or fat headers.
+     * In the common successful case, this header is generally zero.
+     * In less common cases, it's a small negative number and will be
+     * followed by an error string.
+     *
+     * This exists purely for android.database.DatabaseUtils and
+     * insulating it from having to handle fat headers as returned by
+     * e.g. StrictMode-induced RPC responses.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public final int readExceptionCode() {
+        int code = readInt();
+        if (code == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
+            AppOpsManager.readAndLogNotedAppops(this);
+            // Read next header or real exception if there is no more header
+            code = readInt();
+        }
+
+        if (code == EX_HAS_STRICTMODE_REPLY_HEADER) {
+            int headerSize = readInt();
+            if (headerSize == 0) {
+                Log.e(TAG, "Unexpected zero-sized Parcel reply header.");
+            } else {
+                // Currently the only thing in the header is StrictMode stacks,
+                // but discussions around event/RPC tracing suggest we might
+                // put that here too.  If so, switch on sub-header tags here.
+                // But for now, just parse out the StrictMode stuff.
+                StrictMode.readAndHandleBinderCallViolations(this);
+            }
+            // And fat response headers are currently only used when
+            // there are no exceptions, so return no error:
+            return 0;
+        }
+        return code;
+    }
+
+    /**
+     * Throw an exception with the given message. Not intended for use
+     * outside the Parcel class.
+     *
+     * @param code Used to determine which exception class to throw.
+     * @param msg The exception message.
+     */
+    public final void readException(int code, String msg) {
+        String remoteStackTrace = null;
+        final int remoteStackPayloadSize = readInt();
+        if (remoteStackPayloadSize > 0) {
+            remoteStackTrace = readString();
+        }
+        Exception e = createException(code, msg);
+        // Attach remote stack trace if availalble
+        if (remoteStackTrace != null) {
+            RemoteException cause = new RemoteException(
+                    "Remote stack trace:\n" + remoteStackTrace, null, false, false);
+            ExceptionUtils.appendCause(e, cause);
+        }
+        SneakyThrow.sneakyThrow(e);
+    }
+
+    /**
+     * Creates an exception with the given message.
+     *
+     * @param code Used to determine which exception class to throw.
+     * @param msg The exception message.
+     */
+    private Exception createException(int code, String msg) {
+        Exception exception = createExceptionOrNull(code, msg);
+        return exception != null
+                ? exception
+                : new RuntimeException("Unknown exception code: " + code + " msg " + msg);
+    }
+
+    /** @hide */
+    public Exception createExceptionOrNull(int code, String msg) {
+        switch (code) {
+            case EX_PARCELABLE:
+                if (readInt() > 0) {
+                    return (Exception) readParcelable(Parcelable.class.getClassLoader(), java.lang.Exception.class);
+                } else {
+                    return new RuntimeException(msg + " [missing Parcelable]");
+                }
+            case EX_SECURITY:
+                return new SecurityException(msg);
+            case EX_BAD_PARCELABLE:
+                return new BadParcelableException(msg);
+            case EX_ILLEGAL_ARGUMENT:
+                return new IllegalArgumentException(msg);
+            case EX_NULL_POINTER:
+                return new NullPointerException(msg);
+            case EX_ILLEGAL_STATE:
+                return new IllegalStateException(msg);
+            case EX_NETWORK_MAIN_THREAD:
+                return new NetworkOnMainThreadException();
+            case EX_UNSUPPORTED_OPERATION:
+                return new UnsupportedOperationException(msg);
+            case EX_SERVICE_SPECIFIC:
+                return new ServiceSpecificException(readInt(), msg);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Read an integer value from the parcel at the current dataPosition().
+     */
+    public final int readInt() {
+        return nativeReadInt(mNativePtr);
+    }
+
+    /**
+     * Read a long integer value from the parcel at the current dataPosition().
+     */
+    public final long readLong() {
+        return nativeReadLong(mNativePtr);
+    }
+
+    /**
+     * Read a floating point value from the parcel at the current
+     * dataPosition().
+     */
+    public final float readFloat() {
+        return nativeReadFloat(mNativePtr);
+    }
+
+    /**
+     * Read a double precision floating point value from the parcel at the
+     * current dataPosition().
+     */
+    public final double readDouble() {
+        return nativeReadDouble(mNativePtr);
+    }
+
+    /**
+     * Read a string value from the parcel at the current dataPosition().
+     */
+    @Nullable
+    public final String readString() {
+        return readString16();
+    }
+
+    /** {@hide} */
+    public final @Nullable String readString8() {
+        return mReadWriteHelper.readString8(this);
+    }
+
+    /** {@hide} */
+    public final @Nullable String readString16() {
+        return mReadWriteHelper.readString16(this);
+    }
+
+    /**
+     * Read a string without going though a {@link ReadWriteHelper}.  Subclasses of
+     * {@link ReadWriteHelper} must use this method instead of {@link #readString} to avoid
+     * infinity recursive calls.
+     *
+     * @hide
+     */
+    public @Nullable String readStringNoHelper() {
+        return readString16NoHelper();
+    }
+
+    /** {@hide} */
+    public @Nullable String readString8NoHelper() {
+        return nativeReadString8(mNativePtr);
+    }
+
+    /** {@hide} */
+    public @Nullable String readString16NoHelper() {
+        return nativeReadString16(mNativePtr);
+    }
+
+    /**
+     * Read a boolean value from the parcel at the current dataPosition().
+     */
+    public final boolean readBoolean() {
+        return readInt() != 0;
+    }
+
+    /**
+     * Read a CharSequence value from the parcel at the current dataPosition().
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Nullable
+    public final CharSequence readCharSequence() {
+        return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this);
+    }
+
+    /**
+     * Read an object from the parcel at the current dataPosition().
+     */
+    public final IBinder readStrongBinder() {
+        final IBinder result = nativeReadStrongBinder(mNativePtr);
+
+        // If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
+        // from the object that returned it.
+        if (result != null && hasFlags(
+                FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT | FLAG_PROPAGATE_ALLOW_BLOCKING)) {
+            Binder.allowBlocking(result);
+        }
+        return result;
+    }
+
+    /**
+     * Read a FileDescriptor from the parcel at the current dataPosition().
+     */
+    public final ParcelFileDescriptor readFileDescriptor() {
+        FileDescriptor fd = nativeReadFileDescriptor(mNativePtr);
+        return fd != null ? new ParcelFileDescriptor(fd) : null;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public final FileDescriptor readRawFileDescriptor() {
+        return nativeReadFileDescriptor(mNativePtr);
+    }
+
+    /**
+     * {@hide}
+     * Read and return a new array of FileDescriptors from the parcel.
+     * @return the FileDescriptor array, or null if the array is null.
+     **/
+    @Nullable
+    public final FileDescriptor[] createRawFileDescriptorArray() {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        FileDescriptor[] f = new FileDescriptor[N];
+        for (int i = 0; i < N; i++) {
+            f[i] = readRawFileDescriptor();
+        }
+        return f;
+    }
+
+    /**
+     * {@hide}
+     * Read an array of FileDescriptors from a parcel.
+     * The passed array must be exactly the length of the array in the parcel.
+     * @return the FileDescriptor array, or null if the array is null.
+     **/
+    public final void readRawFileDescriptorArray(FileDescriptor[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readRawFileDescriptor();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    /**
+     * Read a byte value from the parcel at the current dataPosition().
+     */
+    public final byte readByte() {
+        return (byte)(readInt() & 0xff);
+    }
+
+    /**
+     * Please use {@link #readBundle(ClassLoader)} instead (whose data must have
+     * been written with {@link #writeBundle}.  Read into an existing Map object
+     * from the parcel at the current dataPosition().
+     *
+     * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this
+     *      method is still preferred use the type-safer version {@link #readMap(Map, ClassLoader,
+     *      Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
+        readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
+    }
+
+    /**
+     * Same as {@link #readMap(Map, ClassLoader)} but accepts {@code clazzKey} and
+     * {@code clazzValue} parameter as the types required for each key and value pair.
+     *
+     * @throws BadParcelableException If the item to be deserialized is not an instance of that
+     * class or any of its children class
+     */
+    public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
+            @Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
+            @NonNull Class<V> clazzValue) {
+        Objects.requireNonNull(clazzKey);
+        Objects.requireNonNull(clazzValue);
+        readMapInternal(outVal, loader, clazzKey, clazzValue);
+    }
+
+    /**
+     * Read into an existing List object from the parcel at the current
+     * dataPosition(), using the given class loader to load any enclosed
+     * Parcelables.  If it is null, the default class loader is used.
+     *
+     * @deprecated Use the type-safer version {@link #readList(List, ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the items'
+     *      class is final) since this is also more performant. Note that changing to the latter
+     *      also requires changing the writes.
+     */
+    @Deprecated
+    public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
+        int N = readInt();
+        readListInternal(outVal, N, loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readList(List, ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readList(List, ClassLoader)} instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    public <T> void readList(@NonNull List<? super T> outVal,
+            @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        int n = readInt();
+        readListInternal(outVal, n, loader, clazz);
+    }
+
+    /**
+     * Please use {@link #readBundle(ClassLoader)} instead (whose data must have
+     * been written with {@link #writeBundle}.  Read and return a new HashMap
+     * object from the parcel at the current dataPosition(), using the given
+     * class loader to load any enclosed Parcelables.  Returns null if
+     * the previously written map object was null.
+     *
+     * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this
+     *      method is still preferred use the type-safer version {@link #readHashMap(ClassLoader,
+     *      Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Nullable
+    public HashMap readHashMap(@Nullable ClassLoader loader) {
+        return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
+    }
+
+    /**
+     * Same as {@link #readHashMap(ClassLoader)} but accepts {@code clazzKey} and
+     * {@code clazzValue} parameter as the types required for each key and value pair.
+     *
+     * @throws BadParcelableException if the item to be deserialized is not an instance of that
+     * class or any of its children class
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    @Nullable
+    public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
+            @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+        Objects.requireNonNull(clazzKey);
+        Objects.requireNonNull(clazzValue);
+        return readHashMapInternal(loader, clazzKey, clazzValue);
+    }
+
+    /**
+     * Read and return a new Bundle object from the parcel at the current
+     * dataPosition().  Returns null if the previously written Bundle object was
+     * null.
+     */
+    @Nullable
+    public final Bundle readBundle() {
+        return readBundle(null);
+    }
+
+    /**
+     * Read and return a new Bundle object from the parcel at the current
+     * dataPosition(), using the given class loader to initialize the class
+     * loader of the Bundle for later retrieval of Parcelable objects.
+     * Returns null if the previously written Bundle object was null.
+     */
+    @Nullable
+    public final Bundle readBundle(@Nullable ClassLoader loader) {
+        int length = readInt();
+        if (length < 0) {
+            if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
+            return null;
+        }
+
+        final Bundle bundle = new Bundle(this, length);
+        if (loader != null) {
+            bundle.setClassLoader(loader);
+        }
+        return bundle;
+    }
+
+    /**
+     * Read and return a new Bundle object from the parcel at the current
+     * dataPosition().  Returns null if the previously written Bundle object was
+     * null.
+     */
+    @Nullable
+    public final PersistableBundle readPersistableBundle() {
+        return readPersistableBundle(null);
+    }
+
+    /**
+     * Read and return a new Bundle object from the parcel at the current
+     * dataPosition(), using the given class loader to initialize the class
+     * loader of the Bundle for later retrieval of Parcelable objects.
+     * Returns null if the previously written Bundle object was null.
+     */
+    @Nullable
+    public final PersistableBundle readPersistableBundle(@Nullable ClassLoader loader) {
+        int length = readInt();
+        if (length < 0) {
+            if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
+            return null;
+        }
+
+        final PersistableBundle bundle = new PersistableBundle(this, length);
+        if (loader != null) {
+            bundle.setClassLoader(loader);
+        }
+        return bundle;
+    }
+
+    /**
+     * Read a Size from the parcel at the current dataPosition().
+     */
+    @NonNull
+    public final Size readSize() {
+        final int width = readInt();
+        final int height = readInt();
+        return new Size(width, height);
+    }
+
+    /**
+     * Read a SizeF from the parcel at the current dataPosition().
+     */
+    @NonNull
+    public final SizeF readSizeF() {
+        final float width = readFloat();
+        final float height = readFloat();
+        return new SizeF(width, height);
+    }
+
+    /**
+     * Read and return a byte[] object from the parcel.
+     */
+    @Nullable
+    public final byte[] createByteArray() {
+        return nativeCreateByteArray(mNativePtr);
+    }
+
+    /**
+     * Read a byte[] object from the parcel and copy it into the
+     * given byte array.
+     */
+    public final void readByteArray(@NonNull byte[] val) {
+        boolean valid = nativeReadByteArray(mNativePtr, val, (val != null) ? val.length : 0);
+        if (!valid) {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    /**
+     * Read a blob of data from the parcel and return it as a byte array.
+     * @see #writeBlob(byte[], int, int)
+     */
+    @Nullable
+    public final byte[] readBlob() {
+        return nativeReadBlob(mNativePtr);
+    }
+
+    /**
+     * Read and return a String[] object from the parcel.
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    @Nullable
+    public final String[] readStringArray() {
+        return createString16Array();
+    }
+
+    /**
+     * Read and return a CharSequence[] object from the parcel.
+     * {@hide}
+     */
+    @Nullable
+    public final CharSequence[] readCharSequenceArray() {
+        CharSequence[] array = null;
+
+        int length = readInt();
+        if (length >= 0)
+        {
+            array = new CharSequence[length];
+
+            for (int i = 0 ; i < length ; i++)
+            {
+                array[i] = readCharSequence();
+            }
+        }
+
+        return array;
+    }
+
+    /**
+     * Read and return an ArrayList&lt;CharSequence&gt; object from the parcel.
+     * {@hide}
+     */
+    @Nullable
+    public final ArrayList<CharSequence> readCharSequenceList() {
+        ArrayList<CharSequence> array = null;
+
+        int length = readInt();
+        if (length >= 0) {
+            array = new ArrayList<CharSequence>(length);
+
+            for (int i = 0 ; i < length ; i++) {
+                array.add(readCharSequence());
+            }
+        }
+
+        return array;
+    }
+
+    /**
+     * Read and return a new ArrayList object from the parcel at the current
+     * dataPosition().  Returns null if the previously written list object was
+     * null.  The given class loader will be used to load any enclosed
+     * Parcelables.
+     *
+     * @deprecated Use the type-safer version {@link #readArrayList(ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link #createTypedArrayList(Parcelable.Creator)} if possible (eg. if the items'
+     *      class is final) since this is also more performant. Note that changing to the latter
+     *      also requires changing the writes.
+     */
+    @Deprecated
+    @Nullable
+    public ArrayList readArrayList(@Nullable ClassLoader loader) {
+        return readArrayListInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readArrayList(ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readArrayList(ClassLoader)} instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    @Nullable
+    public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
+            @NonNull Class<? extends T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readArrayListInternal(loader, clazz);
+    }
+
+    /**
+     * Read and return a new Object array from the parcel at the current
+     * dataPosition().  Returns null if the previously written array was
+     * null.  The given class loader will be used to load any enclosed
+     * Parcelables.
+     *
+     * @deprecated Use the type-safer version {@link #readArray(ClassLoader, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to use
+     *      {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the items' class is
+     *      final) since this is also more performant. Note that changing to the latter also
+     *      requires changing the writes.
+     */
+    @Deprecated
+    @Nullable
+    public Object[] readArray(@Nullable ClassLoader loader) {
+        return readArrayInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readArray(ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readArray(ClassLoader)} instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @Nullable
+    public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readArrayInternal(loader, clazz);
+    }
+
+    /**
+     * Read and return a new SparseArray object from the parcel at the current
+     * dataPosition().  Returns null if the previously written list object was
+     * null.  The given class loader will be used to load any enclosed
+     * Parcelables.
+     *
+     * @deprecated Use the type-safer version {@link #readSparseArray(ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link #createTypedSparseArray(Parcelable.Creator)} if possible (eg. if the items'
+     *      class is final) since this is also more performant. Note that changing to the latter
+     *      also requires changing the writes.
+     */
+    @Deprecated
+    @Nullable
+    public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
+        return readSparseArrayInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readSparseArray(ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readSparseArray(ClassLoader)} instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @Nullable
+    public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
+            @NonNull Class<? extends T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readSparseArrayInternal(loader, clazz);
+    }
+
+    /**
+     * Read and return a new SparseBooleanArray object from the parcel at the current
+     * dataPosition().  Returns null if the previously written list object was
+     * null.
+     */
+    @Nullable
+    public final SparseBooleanArray readSparseBooleanArray() {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        SparseBooleanArray sa = new SparseBooleanArray(N);
+        readSparseBooleanArrayInternal(sa, N);
+        return sa;
+    }
+
+    /**
+     * Read and return a new SparseIntArray object from the parcel at the current
+     * dataPosition(). Returns null if the previously written array object was null.
+     * @hide
+     */
+    @Nullable
+    public final SparseIntArray readSparseIntArray() {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        SparseIntArray sa = new SparseIntArray(N);
+        readSparseIntArrayInternal(sa, N);
+        return sa;
+    }
+
+    /**
+     * Read and return a new ArrayList containing a particular object type from
+     * the parcel that was written with {@link #writeTypedList} at the
+     * current dataPosition().  Returns null if the
+     * previously written list object was null.  The list <em>must</em> have
+     * previously been written via {@link #writeTypedList} with the same object
+     * type.
+     *
+     * @return A newly created ArrayList containing objects with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeTypedList
+     */
+    @Nullable
+    public final <T> ArrayList<T> createTypedArrayList(@NonNull Parcelable.Creator<T> c) {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        ArrayList<T> l = new ArrayList<T>(N);
+        while (N > 0) {
+            l.add(readTypedObject(c));
+            N--;
+        }
+        return l;
+    }
+
+    /**
+     * Read into the given List items containing a particular object type
+     * that were written with {@link #writeTypedList} at the
+     * current dataPosition().  The list <em>must</em> have
+     * previously been written via {@link #writeTypedList} with the same object
+     * type.
+     *
+     * @see #writeTypedList
+     */
+    public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
+        int M = list.size();
+        int N = readInt();
+        int i = 0;
+        for (; i < M && i < N; i++) {
+            list.set(i, readTypedObject(c));
+        }
+        for (; i<N; i++) {
+            list.add(readTypedObject(c));
+        }
+        for (; i<M; i++) {
+            list.remove(N);
+        }
+    }
+
+    /**
+     * Read into a new {@link SparseArray} items containing a particular object type
+     * that were written with {@link #writeTypedSparseArray(SparseArray, int)} at the
+     * current dataPosition().  The list <em>must</em> have previously been written
+     * via {@link #writeTypedSparseArray(SparseArray, int)} with the same object type.
+     *
+     * @param creator The creator to use when for instantiation.
+     *
+     * @return A newly created {@link SparseArray} containing objects with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeTypedSparseArray(SparseArray, int)
+     */
+    public final @Nullable <T extends Parcelable> SparseArray<T> createTypedSparseArray(
+            @NonNull Parcelable.Creator<T> creator) {
+        final int count = readInt();
+        if (count < 0) {
+            return null;
+        }
+        final SparseArray<T> array = new SparseArray<>(count);
+        for (int i = 0; i < count; i++) {
+            final int index = readInt();
+            final T value = readTypedObject(creator);
+            array.append(index, value);
+        }
+        return array;
+    }
+
+    /**
+     * Read into a new {@link ArrayMap} with string keys items containing a particular
+     * object type that were written with {@link #writeTypedArrayMap(ArrayMap, int)} at the
+     * current dataPosition().  The list <em>must</em> have previously been written
+     * via {@link #writeTypedArrayMap(ArrayMap, int)} with the same object type.
+     *
+     * @param creator The creator to use when for instantiation.
+     *
+     * @return A newly created {@link ArrayMap} containing objects with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeTypedArrayMap(ArrayMap, int)
+     */
+    public final @Nullable <T extends Parcelable> ArrayMap<String, T> createTypedArrayMap(
+            @NonNull Parcelable.Creator<T> creator) {
+        final int count = readInt();
+        if (count < 0) {
+            return null;
+        }
+        final ArrayMap<String, T> map = new ArrayMap<>(count);
+        for (int i = 0; i < count; i++) {
+            final String key = readString();
+            final T value = readTypedObject(creator);
+            map.append(key, value);
+        }
+        return map;
+    }
+
+    /**
+     * Read and return a new ArrayList containing String objects from
+     * the parcel that was written with {@link #writeStringList} at the
+     * current dataPosition().  Returns null if the
+     * previously written list object was null.
+     *
+     * @return A newly created ArrayList containing strings with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeStringList
+     */
+    @Nullable
+    public final ArrayList<String> createStringArrayList() {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        ArrayList<String> l = new ArrayList<String>(N);
+        while (N > 0) {
+            l.add(readString());
+            N--;
+        }
+        return l;
+    }
+
+    /**
+     * Read and return a new ArrayList containing IBinder objects from
+     * the parcel that was written with {@link #writeBinderList} at the
+     * current dataPosition().  Returns null if the
+     * previously written list object was null.
+     *
+     * @return A newly created ArrayList containing strings with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeBinderList
+     */
+    @Nullable
+    public final ArrayList<IBinder> createBinderArrayList() {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        ArrayList<IBinder> l = new ArrayList<IBinder>(N);
+        while (N > 0) {
+            l.add(readStrongBinder());
+            N--;
+        }
+        return l;
+    }
+
+    /**
+     * Read and return a new ArrayList containing T (IInterface) objects from
+     * the parcel that was written with {@link #writeInterfaceList} at the
+     * current dataPosition().  Returns null if the
+     * previously written list object was null.
+     *
+     * @return A newly created ArrayList containing T (IInterface)
+     *
+     * @see #writeInterfaceList
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    @Nullable
+    public final <T extends IInterface> ArrayList<T> createInterfaceArrayList(
+            @NonNull Function<IBinder, T> asInterface) {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        ArrayList<T> l = new ArrayList<T>(N);
+        while (N > 0) {
+            l.add(asInterface.apply(readStrongBinder()));
+            N--;
+        }
+        return l;
+    }
+
+    /**
+     * Read into the given List items String objects that were written with
+     * {@link #writeStringList} at the current dataPosition().
+     *
+     * @see #writeStringList
+     */
+    public final void readStringList(@NonNull List<String> list) {
+        int M = list.size();
+        int N = readInt();
+        int i = 0;
+        for (; i < M && i < N; i++) {
+            list.set(i, readString());
+        }
+        for (; i<N; i++) {
+            list.add(readString());
+        }
+        for (; i<M; i++) {
+            list.remove(N);
+        }
+    }
+
+    /**
+     * Read into the given List items IBinder objects that were written with
+     * {@link #writeBinderList} at the current dataPosition().
+     *
+     * @see #writeBinderList
+     */
+    public final void readBinderList(@NonNull List<IBinder> list) {
+        int M = list.size();
+        int N = readInt();
+        int i = 0;
+        for (; i < M && i < N; i++) {
+            list.set(i, readStrongBinder());
+        }
+        for (; i<N; i++) {
+            list.add(readStrongBinder());
+        }
+        for (; i<M; i++) {
+            list.remove(N);
+        }
+    }
+
+    /**
+     * Read into the given List items IInterface objects that were written with
+     * {@link #writeInterfaceList} at the current dataPosition().
+     *
+     * @see #writeInterfaceList
+     */
+    public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list,
+            @NonNull Function<IBinder, T> asInterface) {
+        int M = list.size();
+        int N = readInt();
+        int i = 0;
+        for (; i < M && i < N; i++) {
+            list.set(i, asInterface.apply(readStrongBinder()));
+        }
+        for (; i<N; i++) {
+            list.add(asInterface.apply(readStrongBinder()));
+        }
+        for (; i<M; i++) {
+            list.remove(N);
+        }
+    }
+
+    /**
+     * Read the list of {@code Parcelable} objects at the current data position into the
+     * given {@code list}. The contents of the {@code list} are replaced. If the serialized
+     * list was {@code null}, {@code list} is cleared.
+     *
+     * @see #writeParcelableList(List, int)
+     *
+     * @deprecated Use the type-safer version {@link #readParcelableList(List, ClassLoader, Class)}
+     *      starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the
+     *      format to use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the
+     *      items' class is final) since this is also more performant. Note that changing to the
+     *      latter also requires changing the writes.
+     */
+    @Deprecated
+    @NonNull
+    public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
+            @Nullable ClassLoader cl) {
+        return readParcelableListInternal(list, cl, /*clazz*/ null);
+    }
+
+    /**
+     * Same as {@link #readParcelableList(List, ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * <p><b>Warning: </b> if the list contains items implementing the {@link Parcelable} interface,
+     * the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readParcelableList(List, ClassLoader)} instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @NonNull
+    public <T> List<T> readParcelableList(@NonNull List<T> list,
+            @Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) {
+        Objects.requireNonNull(list);
+        Objects.requireNonNull(clazz);
+        return readParcelableListInternal(list, cl, clazz);
+    }
+
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @NonNull
+    private <T> List<T> readParcelableListInternal(@NonNull List<T> list,
+            @Nullable ClassLoader cl, @Nullable Class<? extends T> clazz) {
+        final int n = readInt();
+        if (n == -1) {
+            list.clear();
+            return list;
+        }
+
+        final int m = list.size();
+        int i = 0;
+        for (; i < m && i < n; i++) {
+            list.set(i, (T) readParcelableInternal(cl, clazz));
+        }
+        for (; i < n; i++) {
+            list.add((T) readParcelableInternal(cl, clazz));
+        }
+        for (; i < m; i++) {
+            list.remove(n);
+        }
+        return list;
+    }
+
+    /**
+     * Read and return a new array containing a particular object type from
+     * the parcel at the current dataPosition().  Returns null if the
+     * previously written array was null.  The array <em>must</em> have
+     * previously been written via {@link #writeTypedArray} with the same
+     * object type.
+     *
+     * @return A newly created array containing objects with the same data
+     *         as those that were previously written.
+     *
+     * @see #writeTypedArray
+     */
+    @Nullable
+    public final <T> T[] createTypedArray(@NonNull Parcelable.Creator<T> c) {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        T[] l = c.newArray(N);
+        for (int i=0; i<N; i++) {
+            l[i] = readTypedObject(c);
+        }
+        return l;
+    }
+
+    public final <T> void readTypedArray(@NonNull T[] val, @NonNull Parcelable.Creator<T> c) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readTypedObject(c);
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+    /**
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    public final <T> T[] readTypedArray(Parcelable.Creator<T> c) {
+        return createTypedArray(c);
+    }
+
+    /**
+     * Read and return a typed Parcelable object from a parcel.
+     * Returns null if the previous written object was null.
+     * The object <em>must</em> have previous been written via
+     * {@link #writeTypedObject} with the same object type.
+     *
+     * @return A newly created object of the type that was previously
+     *         written.
+     *
+     * @see #writeTypedObject
+     */
+    @Nullable
+    public final <T> T readTypedObject(@NonNull Parcelable.Creator<T> c) {
+        if (readInt() != 0) {
+            return c.createFromParcel(this);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Read a new multi-dimensional array from a parcel.  If you want to read Parcelable or
+     * IInterface values, use {@link #readFixedArray(Object, Parcelable.Creator)} or
+     * {@link #readFixedArray(Object, Function)}.
+     * @param val the destination array to hold the read values.
+     *
+     * @see #writeTypedArray
+     * @see #readBooleanArray
+     * @see #readByteArray
+     * @see #readCharArray
+     * @see #readIntArray
+     * @see #readLongArray
+     * @see #readFloatArray
+     * @see #readDoubleArray
+     * @see #readBinderArray
+     * @see #readInterfaceArray
+     * @see #readTypedArray
+     */
+    public <T> void readFixedArray(@NonNull T val) {
+        Class<?> componentType = val.getClass().getComponentType();
+        if (componentType == boolean.class) {
+            readBooleanArray((boolean[]) val);
+        } else if (componentType == byte.class) {
+            readByteArray((byte[]) val);
+        } else if (componentType == char.class) {
+            readCharArray((char[]) val);
+        } else if (componentType == int.class) {
+            readIntArray((int[]) val);
+        } else if (componentType == long.class) {
+            readLongArray((long[]) val);
+        } else if (componentType == float.class) {
+            readFloatArray((float[]) val);
+        } else if (componentType == double.class) {
+            readDoubleArray((double[]) val);
+        } else if (componentType == IBinder.class) {
+            readBinderArray((IBinder[]) val);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length != Array.getLength(val)) {
+                throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+                    + ", but got " + length);
+            }
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i));
+            }
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    /**
+     * Read a new multi-dimensional array of typed interfaces from a parcel.
+     * If you want to read Parcelable values, use
+     * {@link #readFixedArray(Object, Parcelable.Creator)}. For values of other types, use
+     * {@link #readFixedArray(Object)}.
+     * @param val the destination array to hold the read values.
+     */
+    public <T, S extends IInterface> void readFixedArray(@NonNull T val,
+            @NonNull Function<IBinder, S> asInterface) {
+        Class<?> componentType = val.getClass().getComponentType();
+        if (IInterface.class.isAssignableFrom(componentType)) {
+            readInterfaceArray((S[]) val, asInterface);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length != Array.getLength(val)) {
+                throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+                    + ", but got " + length);
+            }
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), asInterface);
+            }
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    /**
+     * Read a new multi-dimensional array of typed parcelables from a parcel.
+     * If you want to read IInterface values, use
+     * {@link #readFixedArray(Object, Function)}. For values of other types, use
+     * {@link #readFixedArray(Object)}.
+     * @param val the destination array to hold the read values.
+     */
+    public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
+            @NonNull Parcelable.Creator<S> c) {
+        Class<?> componentType = val.getClass().getComponentType();
+        if (Parcelable.class.isAssignableFrom(componentType)) {
+            readTypedArray((S[]) val, c);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length != Array.getLength(val)) {
+                throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+                    + ", but got " + length);
+            }
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), c);
+            }
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+    }
+
+    private void ensureClassHasExpectedDimensions(@NonNull Class<?> cls, int numDimension) {
+        if (numDimension <= 0) {
+            throw new BadParcelableException("Fixed-size array should have dimensions.");
+        }
+
+        for (int i = 0; i < numDimension; i++) {
+            if (!cls.isArray()) {
+                throw new BadParcelableException("Array has fewer dimensions than expected: "
+                    + numDimension);
+            }
+            cls = cls.getComponentType();
+        }
+        if (cls.isArray()) {
+            throw new BadParcelableException("Array has more dimensions than expected: "
+                + numDimension);
+        }
+    }
+
+    /**
+     * Read and return a new multi-dimensional array from a parcel.  Returns null if the
+     * previously written array object is null.  If you want to read Parcelable or
+     * IInterface values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])} or
+     * {@link #createFixedArray(Class, Function, int[])}.
+     * @param cls  the Class object for the target array type. (e.g. int[][].class)
+     * @param dimensions an array of int representing length of each dimension.
+     *
+     * @see #writeTypedArray
+     * @see #createBooleanArray
+     * @see #createByteArray
+     * @see #createCharArray
+     * @see #createIntArray
+     * @see #createLongArray
+     * @see #createFloatArray
+     * @see #createDoubleArray
+     * @see #createBinderArray
+     * @see #createInterfaceArray
+     * @see #createTypedArray
+     */
+    @Nullable
+    public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
+        // Check if type matches with dimensions
+        // If type is one-dimensional array, delegate to other creators
+        // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+        ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+        T val = null;
+        final Class<?> componentType = cls.getComponentType();
+        if (componentType == boolean.class) {
+            val = (T) createBooleanArray();
+        } else if (componentType == byte.class) {
+            val = (T) createByteArray();
+        } else if (componentType == char.class) {
+            val = (T) createCharArray();
+        } else if (componentType == int.class) {
+            val = (T) createIntArray();
+        } else if (componentType == long.class) {
+            val = (T) createLongArray();
+        } else if (componentType == float.class) {
+            val = (T) createFloatArray();
+        } else if (componentType == double.class) {
+            val = (T) createDoubleArray();
+        } else if (componentType == IBinder.class) {
+            val = (T) createBinderArray();
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length < 0) {
+                return null;
+            }
+            if (length != dimensions[0]) {
+                throw new BadParcelableException("Bad length: expected " + dimensions[0]
+                    + ", but got " + length);
+            }
+
+            // Create a multi-dimensional array with an innermost component type and dimensions
+            Class<?> innermost = componentType.getComponentType();
+            while (innermost.isArray()) {
+                innermost = innermost.getComponentType();
+            }
+            val = (T) Array.newInstance(innermost, dimensions);
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i));
+            }
+            return val;
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+
+        // Check if val is null (which is OK) or has the expected size.
+        // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+        // are created with expected dimensions.
+        if (val != null && Array.getLength(val) != dimensions[0]) {
+            throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+                + Array.getLength(val));
+        }
+        return val;
+    }
+
+    /**
+     * Read and return a new multi-dimensional array of typed interfaces from a parcel.
+     * Returns null if the previously written array object is null.  If you want to read
+     * Parcelable values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])}.
+     * For values of other types use {@link #createFixedArray(Class, int[])}.
+     * @param cls  the Class object for the target array type. (e.g. IFoo[][].class)
+     * @param dimensions an array of int representing length of each dimension.
+     */
+    @Nullable
+    public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
+            @NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
+        // Check if type matches with dimensions
+        // If type is one-dimensional array, delegate to other creators
+        // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+        ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+        T val = null;
+        final Class<?> componentType = cls.getComponentType();
+        if (IInterface.class.isAssignableFrom(componentType)) {
+            val = (T) createInterfaceArray(n -> (S[]) Array.newInstance(componentType, n),
+                    asInterface);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length < 0) {
+                return null;
+            }
+            if (length != dimensions[0]) {
+                throw new BadParcelableException("Bad length: expected " + dimensions[0]
+                    + ", but got " + length);
+            }
+
+            // Create a multi-dimensional array with an innermost component type and dimensions
+            Class<?> innermost = componentType.getComponentType();
+            while (innermost.isArray()) {
+                innermost = innermost.getComponentType();
+            }
+            val = (T) Array.newInstance(innermost, dimensions);
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), asInterface);
+            }
+            return val;
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+
+        // Check if val is null (which is OK) or has the expected size.
+        // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+        // are created with expected dimensions.
+        if (val != null && Array.getLength(val) != dimensions[0]) {
+            throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+                + Array.getLength(val));
+        }
+        return val;
+    }
+
+    /**
+     * Read and return a new multi-dimensional array of typed parcelables from a parcel.
+     * Returns null if the previously written array object is null.  If you want to read
+     * IInterface values, use {@link #createFixedArray(Class, Function, int[])}.
+     * For values of other types use {@link #createFixedArray(Class, int[])}.
+     * @param cls  the Class object for the target array type. (e.g. Foo[][].class)
+     * @param dimensions an array of int representing length of each dimension.
+     */
+    @Nullable
+    public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
+            @NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
+        // Check if type matches with dimensions
+        // If type is one-dimensional array, delegate to other creators
+        // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+        ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+        T val = null;
+        final Class<?> componentType = cls.getComponentType();
+        if (Parcelable.class.isAssignableFrom(componentType)) {
+            val = (T) createTypedArray(c);
+        } else if (componentType.isArray()) {
+            int length = readInt();
+            if (length < 0) {
+                return null;
+            }
+            if (length != dimensions[0]) {
+                throw new BadParcelableException("Bad length: expected " + dimensions[0]
+                    + ", but got " + length);
+            }
+
+            // Create a multi-dimensional array with an innermost component type and dimensions
+            Class<?> innermost = componentType.getComponentType();
+            while (innermost.isArray()) {
+                innermost = innermost.getComponentType();
+            }
+            val = (T) Array.newInstance(innermost, dimensions);
+            for (int i = 0; i < length; i++) {
+                readFixedArray(Array.get(val, i), c);
+            }
+            return val;
+        } else {
+            throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+        }
+
+        // Check if val is null (which is OK) or has the expected size.
+        // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+        // are created with expected dimensions.
+        if (val != null && Array.getLength(val) != dimensions[0]) {
+            throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+                + Array.getLength(val));
+        }
+        return val;
+    }
+
+    /**
+     * Write a heterogeneous array of Parcelable objects into the Parcel.
+     * Each object in the array is written along with its class name, so
+     * that the correct class can later be instantiated.  As a result, this
+     * has significantly more overhead than {@link #writeTypedArray}, but will
+     * correctly handle an array containing more than one type of object.
+     *
+     * @param value The array of objects to be written.
+     * @param parcelableFlags Contextual flags as per
+     * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     *
+     * @see #writeTypedArray
+     */
+    public final <T extends Parcelable> void writeParcelableArray(@Nullable T[] value,
+            int parcelableFlags) {
+        if (value != null) {
+            int N = value.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeParcelable(value[i], parcelableFlags);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * Read a typed object from a parcel.  The given class loader will be
+     * used to load any enclosed Parcelables.  If it is null, the default class
+     * loader will be used.
+     */
+    @Nullable
+    public final Object readValue(@Nullable ClassLoader loader) {
+        return readValue(loader, /* clazz */ null);
+    }
+
+
+    /**
+     * @see #readValue(int, ClassLoader, Class, Class[])
+     */
+    @Nullable
+    private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz,
+            @Nullable Class<?>... itemTypes) {
+        int type = readInt();
+        final T object;
+        if (isLengthPrefixed(type)) {
+            int length = readInt();
+            int start = dataPosition();
+            object = readValue(type, loader, clazz, itemTypes);
+            int actual = dataPosition() - start;
+            if (actual != length) {
+                Slog.wtfStack(TAG,
+                        "Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type)
+                                + "  consumed " + actual + " bytes, but " + length + " expected.");
+            }
+        } else {
+            object = readValue(type, loader, clazz, itemTypes);
+        }
+        return object;
+    }
+
+    /**
+     * This will return a {@link BiFunction} for length-prefixed types that deserializes the object
+     * when {@link BiFunction#apply} is called (the arguments correspond to the ones of {@link
+     * #readValue(int, ClassLoader, Class, Class[])} after the class loader), for other types it
+     * will return the object itself.
+     *
+     * <p>After calling {@link BiFunction#apply} the parcel cursor will not change. Note that you
+     * shouldn't recycle the parcel, not at least until all objects have been retrieved. No
+     * synchronization attempts are made.
+     *
+     * </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
+     * function objects are equal if either of the following is true:
+     * <ul>
+     *   <li>{@link BiFunction#apply} has been called on both and both objects returned are equal.
+     *   <li>{@link BiFunction#apply} hasn't been called on either one and everything below is true:
+     *   <ul>
+     *       <li>The {@code loader} parameters used to retrieve each are equal.
+     *       <li>They both have the same type.
+     *       <li>They have the same payload length.
+     *       <li>Their binary content is the same.
+     *   </ul>
+     * </ul>
+     *
+     * @hide
+     */
+    @Nullable
+    public Object readLazyValue(@Nullable ClassLoader loader) {
+        int start = dataPosition();
+        int type = readInt();
+        if (isLengthPrefixed(type)) {
+            int objectLength = readInt();
+            if (objectLength < 0) {
+                return null;
+            }
+            int end = MathUtils.addOrThrow(dataPosition(), objectLength);
+            int valueLength = end - start;
+            setDataPosition(end);
+            return new LazyValue(this, start, valueLength, type, loader);
+        } else {
+            return readValue(type, loader, /* clazz */ null);
+        }
+    }
+
+
+    private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
+        /**
+         *                      |   4B   |   4B   |
+         * mSource = Parcel{... |  type  | length | object | ...}
+         *                      a        b        c        d
+         * length = d - c
+         * mPosition = a
+         * mLength = d - a
+         */
+        private final int mPosition;
+        private final int mLength;
+        private final int mType;
+        @Nullable private final ClassLoader mLoader;
+        @Nullable private Object mObject;
+
+        /**
+         * This goes from non-null to null once. Always check the nullability of this object before
+         * performing any operations, either involving itself or mObject since the happens-before
+         * established by this volatile will guarantee visibility of either. We can assume this
+         * parcel won't change anymore.
+         */
+        @Nullable private volatile Parcel mSource;
+
+        LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) {
+            mSource = requireNonNull(source);
+            mPosition = position;
+            mLength = length;
+            mType = type;
+            mLoader = loader;
+        }
+
+        @Override
+        public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) {
+            Parcel source = mSource;
+            if (source != null) {
+                synchronized (source) {
+                    // Check mSource != null guarantees callers won't ever see different objects.
+                    if (mSource != null) {
+                        int restore = source.dataPosition();
+                        try {
+                            source.setDataPosition(mPosition);
+                            mObject = source.readValue(mLoader, clazz, itemTypes);
+                        } finally {
+                            source.setDataPosition(restore);
+                        }
+                        mSource = null;
+                    }
+                }
+            }
+            return mObject;
+        }
+
+        public void writeToParcel(Parcel out) {
+            Parcel source = mSource;
+            if (source != null) {
+                synchronized (source) {
+                    if (mSource != null) {
+                        out.appendFrom(source, mPosition, mLength);
+                        return;
+                    }
+                }
+            }
+
+            out.writeValue(mObject);
+        }
+
+        public boolean hasFileDescriptors() {
+            Parcel source = mSource;
+            if (source != null) {
+                synchronized (source) {
+                    if (mSource != null) {
+                        return source.hasFileDescriptors(mPosition, mLength);
+                    }
+                }
+            }
+
+            return Parcel.hasFileDescriptors(mObject);
+        }
+
+        @Override
+        public String toString() {
+            return (mSource != null)
+                    ? "Supplier{" + valueTypeToString(mType) + "@" + mPosition + "+" + mLength + '}'
+                    : "Supplier{" + mObject + "}";
+        }
+
+        /**
+         * We're checking if the *lazy value* is equal to another one, not if the *object*
+         * represented by the lazy value is equal to the other one. So, if there are two lazy values
+         * and one of them has been deserialized but the other hasn't this will always return false.
+         */
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof LazyValue)) {
+                return false;
+            }
+            LazyValue value = (LazyValue) other;
+            // Check if they are either both serialized or both deserialized.
+            Parcel source = mSource;
+            Parcel otherSource = value.mSource;
+            if ((source == null) != (otherSource == null)) {
+                return false;
+            }
+            // If both are deserialized, compare the live objects.
+            if (source == null) {
+                // Note that here it's guaranteed that both mObject references contain valid values
+                // (possibly null) since mSource will have provided the memory barrier for those and
+                // once deserialized we never go back to serialized state.
+                return Objects.equals(mObject, value.mObject);
+            }
+            // Better safely fail here since this could mean we get different objects.
+            if (!Objects.equals(mLoader, value.mLoader)) {
+                return false;
+            }
+            // Otherwise compare metadata prior to comparing payload.
+            if (mType != value.mType || mLength != value.mLength) {
+                return false;
+            }
+            // Finally we compare the payload.
+            return Parcel.compareData(source, mPosition, otherSource, value.mPosition, mLength);
+        }
+
+        @Override
+        public int hashCode() {
+            // Accessing mSource first to provide memory barrier for mObject
+            return Objects.hash(mSource == null, mObject, mLoader, mType, mLength);
+        }
+    }
+
+    /** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
+    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        // Avoids allocating Class[0] array
+        return readValue(type, loader, clazz, (Class<?>[]) null);
+    }
+
+    /**
+     * Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
+     * type first.
+     *
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @param itemTypes If the value is a container, these represent the item types (eg. for a list
+     *                  it's the item type, for a map, it's the key type, followed by the value
+     *                  type).
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz,
+            @Nullable Class<?>... itemTypes) {
+        final Object object;
+        switch (type) {
+            case VAL_NULL:
+                object = null;
+                break;
+
+            case VAL_STRING:
+                object = readString();
+                break;
+
+            case VAL_INTEGER:
+                object = readInt();
+                break;
+
+            case VAL_MAP:
+                checkTypeToUnparcel(clazz, HashMap.class);
+                Class<?> keyType = ArrayUtils.getOrNull(itemTypes, 0);
+                Class<?> valueType = ArrayUtils.getOrNull(itemTypes, 1);
+                checkArgument((keyType == null) == (valueType == null));
+                object = readHashMapInternal(loader, keyType, valueType);
+                break;
+
+            case VAL_PARCELABLE:
+                object = readParcelableInternal(loader, clazz);
+                break;
+
+            case VAL_SHORT:
+                object = (short) readInt();
+                break;
+
+            case VAL_LONG:
+                object = readLong();
+                break;
+
+            case VAL_FLOAT:
+                object = readFloat();
+                break;
+
+            case VAL_DOUBLE:
+                object = readDouble();
+                break;
+
+            case VAL_BOOLEAN:
+                object = readInt() == 1;
+                break;
+
+            case VAL_CHARSEQUENCE:
+                object = readCharSequence();
+                break;
+
+            case VAL_LIST: {
+                checkTypeToUnparcel(clazz, ArrayList.class);
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                object = readArrayListInternal(loader, itemType);
+                break;
+            }
+            case VAL_BOOLEANARRAY:
+                object = createBooleanArray();
+                break;
+
+            case VAL_BYTEARRAY:
+                object = createByteArray();
+                break;
+
+            case VAL_STRINGARRAY:
+                object = readStringArray();
+                break;
+
+            case VAL_CHARSEQUENCEARRAY:
+                object = readCharSequenceArray();
+                break;
+
+            case VAL_IBINDER:
+                object = readStrongBinder();
+                break;
+
+            case VAL_OBJECTARRAY: {
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Object.class);
+                object = readArrayInternal(loader, itemType);
+                break;
+            }
+            case VAL_INTARRAY:
+                object = createIntArray();
+                break;
+
+            case VAL_LONGARRAY:
+                object = createLongArray();
+                break;
+
+            case VAL_BYTE:
+                object = readByte();
+                break;
+
+            case VAL_SERIALIZABLE:
+                object = readSerializableInternal(loader, clazz);
+                break;
+
+            case VAL_PARCELABLEARRAY: {
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Parcelable.class);
+                object = readParcelableArrayInternal(loader, itemType);
+                break;
+            }
+            case VAL_SPARSEARRAY: {
+                checkTypeToUnparcel(clazz, SparseArray.class);
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                object = readSparseArrayInternal(loader, itemType);
+                break;
+            }
+            case VAL_SPARSEBOOLEANARRAY:
+                object = readSparseBooleanArray();
+                break;
+
+            case VAL_BUNDLE:
+                object = readBundle(loader); // loading will be deferred
+                break;
+
+            case VAL_PERSISTABLEBUNDLE:
+                object = readPersistableBundle(loader);
+                break;
+
+            case VAL_SIZE:
+                object = readSize();
+                break;
+
+            case VAL_SIZEF:
+                object = readSizeF();
+                break;
+
+            case VAL_DOUBLEARRAY:
+                object = createDoubleArray();
+                break;
+
+            case VAL_CHAR:
+                object = (char) readInt();
+                break;
+
+            case VAL_SHORTARRAY:
+                object = createShortArray();
+                break;
+
+            case VAL_CHARARRAY:
+                object = createCharArray();
+                break;
+
+            case VAL_FLOATARRAY:
+                object = createFloatArray();
+                break;
+
+            default:
+                int off = dataPosition() - 4;
+                throw new BadParcelableException(
+                    "Parcel " + this + ": Unmarshalling unknown type code " + type
+                            + " at offset " + off);
+        }
+        if (object != null && clazz != null && !clazz.isInstance(object)) {
+            throw new BadTypeParcelableException("Unparcelled object " + object
+                    + " is not an instance of required class " + clazz.getName()
+                    + " provided in the parameter");
+        }
+        return (T) object;
+    }
+
+    private boolean isLengthPrefixed(int type) {
+        // In general, we want custom types and containers of custom types to be length-prefixed,
+        // this allows clients (eg. Bundle) to skip their content during deserialization. The
+        // exception to this is Bundle, since Bundle is already length-prefixed and already copies
+        // the correspondent section of the parcel internally.
+        switch (type) {
+            case VAL_MAP:
+            case VAL_PARCELABLE:
+            case VAL_LIST:
+            case VAL_SPARSEARRAY:
+            case VAL_PARCELABLEARRAY:
+            case VAL_OBJECTARRAY:
+            case VAL_SERIALIZABLE:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks that an array of type T[], where T is {@code componentTypeToUnparcel}, is a subtype of
+     * {@code requiredArrayType}.
+     */
+    private void checkArrayTypeToUnparcel(@Nullable Class<?> requiredArrayType,
+            Class<?> componentTypeToUnparcel) {
+        if (requiredArrayType != null) {
+            // In Java 12, we could use componentTypeToUnparcel.arrayType() for the check
+            Class<?> requiredComponentType = requiredArrayType.getComponentType();
+            if (requiredComponentType == null) {
+                throw new BadTypeParcelableException(
+                        "About to unparcel an array but type "
+                                + requiredArrayType.getCanonicalName()
+                                + " required by caller is not an array.");
+            }
+            checkTypeToUnparcel(requiredComponentType, componentTypeToUnparcel);
+        }
+    }
+
+    /**
+     * Checks that {@code typeToUnparcel} is a subtype of {@code requiredType}, if {@code
+     * requiredType} is not {@code null}.
+     */
+    private void checkTypeToUnparcel(@Nullable Class<?> requiredType, Class<?> typeToUnparcel) {
+        if (requiredType != null && !requiredType.isAssignableFrom(typeToUnparcel)) {
+            throw new BadTypeParcelableException(
+                    "About to unparcel a " + typeToUnparcel.getCanonicalName()
+                            + ", which is not a subtype of type " + requiredType.getCanonicalName()
+                            + " required by caller.");
+        }
+    }
+
+    /**
+     * Read and return a new Parcelable from the parcel.  The given class loader
+     * will be used to load any enclosed Parcelables.  If it is null, the default
+     * class loader will be used.
+     * @param loader A ClassLoader from which to instantiate the Parcelable
+     * object, or null for the default class loader.
+     * @return Returns the newly created Parcelable, or null if a null
+     * object has been written.
+     * @throws BadParcelableException Throws BadParcelableException if there
+     * was an error trying to instantiate the Parcelable.
+     *
+     * @deprecated Use the type-safer version {@link #readParcelable(ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link Parcelable.Creator#createFromParcel(Parcel)} if possible since this is also
+     *      more performant. Note that changing to the latter also requires changing the writes.
+     */
+    @Deprecated
+    @Nullable
+    public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
+        return readParcelableInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readParcelable(ClassLoader)} but accepts {@code clazz} parameter as the type
+     * required for each item.
+     *
+     * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readParcelable(ClassLoader)} instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @Nullable
+    public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readParcelableInternal(loader, clazz);
+    }
+
+    /**
+     * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
+        if (creator == null) {
+            return null;
+        }
+        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+            Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+                    (Parcelable.ClassLoaderCreator<?>) creator;
+            return (T) classLoaderCreator.createFromParcel(this, loader);
+        }
+        return (T) creator.createFromParcel(this);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public final <T extends Parcelable> T readCreator(@NonNull Parcelable.Creator<?> creator,
+            @Nullable ClassLoader loader) {
+        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+          Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+              (Parcelable.ClassLoaderCreator<?>) creator;
+          return (T) classLoaderCreator.createFromParcel(this, loader);
+        }
+        return (T) creator.createFromParcel(this);
+    }
+
+    /**
+     * Read and return a Parcelable.Creator from the parcel. The given class loader will be used to
+     * load the {@link Parcelable.Creator}. If it is null, the default class loader will be used.
+     *
+     * @param loader A ClassLoader from which to instantiate the {@link Parcelable.Creator}
+     * object, or null for the default class loader.
+     * @return the previously written {@link Parcelable.Creator}, or null if a null Creator was
+     * written.
+     * @throws BadParcelableException Throws BadParcelableException if there was an error trying to
+     * read the {@link Parcelable.Creator}.
+     *
+     * @see #writeParcelableCreator
+     *
+     * @deprecated Use the type-safer version {@link #readParcelableCreator(ClassLoader, Class)}
+     *       starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Nullable
+    public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
+        return readParcelableCreatorInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter
+     * as the required type.
+     *
+     * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readParcelableCreator(ClassLoader) instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there there was an error
+     * trying to read the {@link Parcelable.Creator}.
+     */
+    @Nullable
+    public <T> Parcelable.Creator<T> readParcelableCreator(
+            @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readParcelableCreatorInternal(loader, clazz);
+    }
+
+    /**
+     * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> Parcelable.Creator<T> readParcelableCreatorInternal(
+            @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        String name = readString();
+        if (name == null) {
+            return null;
+        }
+
+        Pair<Parcelable.Creator<?>, Class<?>> creatorAndParcelableClass;
+        synchronized (sPairedCreators) {
+            HashMap<String, Pair<Parcelable.Creator<?>, Class<?>>> map =
+                    sPairedCreators.get(loader);
+            if (map == null) {
+                sPairedCreators.put(loader, new HashMap<>());
+                mCreators.put(loader, new HashMap<>());
+                creatorAndParcelableClass = null;
+            } else {
+                creatorAndParcelableClass = map.get(name);
+            }
+        }
+
+        if (creatorAndParcelableClass != null) {
+            Parcelable.Creator<?> creator = creatorAndParcelableClass.first;
+            Class<?> parcelableClass = creatorAndParcelableClass.second;
+            if (clazz != null) {
+                if (!clazz.isAssignableFrom(parcelableClass)) {
+                    throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
+                            + "a subclass of required class " + clazz.getName()
+                            + " provided in the parameter");
+                }
+            }
+
+            return (Parcelable.Creator<T>) creator;
+        }
+
+        Parcelable.Creator<?> creator;
+        Class<?> parcelableClass;
+        try {
+            // If loader == null, explicitly emulate Class.forName(String) "caller
+            // classloader" behavior.
+            ClassLoader parcelableClassLoader =
+                    (loader == null ? getClass().getClassLoader() : loader);
+            // Avoid initializing the Parcelable class until we know it implements
+            // Parcelable and has the necessary CREATOR field. http://b/1171613.
+            parcelableClass = Class.forName(name, false /* initialize */,
+                    parcelableClassLoader);
+            if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
+                throw new BadParcelableException("Parcelable protocol requires subclassing "
+                        + "from Parcelable on class " + name);
+            }
+            if (clazz != null) {
+                if (!clazz.isAssignableFrom(parcelableClass)) {
+                    throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
+                            + "a subclass of required class " + clazz.getName()
+                            + " provided in the parameter");
+                }
+            }
+
+            Field f = parcelableClass.getField("CREATOR");
+            if ((f.getModifiers() & Modifier.STATIC) == 0) {
+                throw new BadParcelableException("Parcelable protocol requires "
+                        + "the CREATOR object to be static on class " + name);
+            }
+            Class<?> creatorType = f.getType();
+            if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
+                // Fail before calling Field.get(), not after, to avoid initializing
+                // parcelableClass unnecessarily.
+                throw new BadParcelableException("Parcelable protocol requires a "
+                        + "Parcelable.Creator object called "
+                        + "CREATOR on class " + name);
+            }
+            creator = (Parcelable.Creator<?>) f.get(null);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
+            throw new BadParcelableException(
+                    "IllegalAccessException when unmarshalling: " + name, e);
+        } catch (ClassNotFoundException e) {
+            Log.e(TAG, "Class not found when unmarshalling: " + name, e);
+            throw new BadParcelableException(
+                    "ClassNotFoundException when unmarshalling: " + name, e);
+        } catch (NoSuchFieldException e) {
+            throw new BadParcelableException("Parcelable protocol requires a "
+                    + "Parcelable.Creator object called "
+                    + "CREATOR on class " + name, e);
+        }
+        if (creator == null) {
+            throw new BadParcelableException("Parcelable protocol requires a "
+                    + "non-null Parcelable.Creator object called "
+                    + "CREATOR on class " + name);
+        }
+
+        synchronized (sPairedCreators) {
+            sPairedCreators.get(loader).put(name, Pair.create(creator, parcelableClass));
+            mCreators.get(loader).put(name, creator);
+        }
+
+        return (Parcelable.Creator<T>) creator;
+    }
+
+    /**
+     * Read and return a new Parcelable array from the parcel.
+     * The given class loader will be used to load any enclosed
+     * Parcelables.
+     * @return the Parcelable array, or null if the array is null
+     *
+     * @deprecated Use the type-safer version {@link #readParcelableArray(ClassLoader, Class)}
+     *      starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the
+     *      format to use {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the
+     *      items' class is final) since this is also more performant. Note that changing to the
+     *      latter also requires changing the writes.
+     */
+    @Deprecated
+    @Nullable
+    public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
+        return readParcelableArrayInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readParcelableArray(ClassLoader)}  but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * <p><b>Warning: </b> the class that implements {@link Parcelable} has to be the immediately
+     * enclosing class of the runtime type of its CREATOR field (that is,
+     * {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
+     * otherwise this method might throw an exception. If the Parcelable class does not enclose the
+     * CREATOR, use the deprecated {@link #readParcelableArray(ClassLoader)} instead.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @Nullable
+    public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        return readParcelableArrayInternal(loader, requireNonNull(clazz));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T[] readParcelableArrayInternal(@Nullable ClassLoader loader,
+            @Nullable Class<T> clazz) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n));
+        for (int i = 0; i < n; i++) {
+            p[i] = readParcelableInternal(loader, clazz);
+        }
+        return p;
+    }
+
+    /**
+     * Read and return a new Serializable object from the parcel.
+     * @return the Serializable object, or null if the Serializable name
+     * wasn't found in the parcel.
+     *
+     * Unlike {@link #readSerializable(ClassLoader, Class)}, it uses the nearest valid class loader
+     * up the execution stack to instantiate the Serializable object.
+     *
+     * @deprecated Use the type-safer version {@link #readSerializable(ClassLoader, Class)} starting
+     *       from Android {@link Build.VERSION_CODES#TIRAMISU}.
+     */
+    @Deprecated
+    @Nullable
+    public Serializable readSerializable() {
+        return readSerializableInternal(/* loader */ null, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readSerializable()} but accepts {@code loader} and {@code clazz} parameters.
+     *
+     * @param loader A ClassLoader from which to instantiate the Serializable object,
+     * or null for the default class loader.
+     * @param clazz The type of the object expected.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children class or there there was an error
+     * deserializing the object.
+     */
+    @Nullable
+    public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readSerializableInternal(
+                loader == null ? getClass().getClassLoader() : loader, clazz);
+    }
+
+    /**
+     * @param clazz The type of the serializable expected or {@code null} for performing no checks
+     */
+    @Nullable
+    private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
+            @Nullable Class<T> clazz) {
+        String name = readString();
+        if (name == null) {
+            // For some reason we were unable to read the name of the Serializable (either there
+            // is nothing left in the Parcel to read, or the next value wasn't a String), so
+            // return null, which indicates that the name wasn't found in the parcel.
+            return null;
+        }
+
+        try {
+            if (clazz != null && loader != null) {
+                // If custom classloader is provided, resolve the type of serializable using the
+                // name, then check the type before deserialization. As in this case we can resolve
+                // the class the same way as ObjectInputStream, using the provided classloader.
+                Class<?> cl = Class.forName(name, false, loader);
+                if (!clazz.isAssignableFrom(cl)) {
+                    throw new BadTypeParcelableException("Serializable object "
+                            + cl.getName() + " is not a subclass of required class "
+                            + clazz.getName() + " provided in the parameter");
+                }
+            }
+            byte[] serializedData = createByteArray();
+            ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
+            ObjectInputStream ois = new ObjectInputStream(bais) {
+                @Override
+                protected Class<?> resolveClass(ObjectStreamClass osClass)
+                        throws IOException, ClassNotFoundException {
+                    // try the custom classloader if provided
+                    if (loader != null) {
+                        Class<?> c = Class.forName(osClass.getName(), false, loader);
+                        return Objects.requireNonNull(c);
+                    }
+                    return super.resolveClass(osClass);
+                }
+            };
+            T object = (T) ois.readObject();
+            if (clazz != null && loader == null) {
+                // If custom classloader is not provided, check the type of the serializable using
+                // the deserialized object, as we cannot resolve the class the same way as
+                // ObjectInputStream.
+                if (!clazz.isAssignableFrom(object.getClass())) {
+                    throw new BadTypeParcelableException("Serializable object "
+                            + object.getClass().getName() + " is not a subclass of required class "
+                            + clazz.getName() + " provided in the parameter");
+                }
+            }
+            return object;
+        } catch (IOException ioe) {
+            throw new BadParcelableException("Parcelable encountered "
+                    + "IOException reading a Serializable object (name = "
+                    + name + ")", ioe);
+        } catch (ClassNotFoundException cnfe) {
+            throw new BadParcelableException("Parcelable encountered "
+                    + "ClassNotFoundException reading a Serializable object (name = "
+                    + name + ")", cnfe);
+        }
+    }
+
+
+    // Left due to the UnsupportedAppUsage. Do not use anymore - use sPairedCreators instead
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private static final HashMap<ClassLoader, HashMap<String, Parcelable.Creator<?>>>
+            mCreators = new HashMap<>();
+
+    // Cache of previously looked up CREATOR.createFromParcel() methods for particular classes.
+    // Keys are the names of the classes, values are a pair consisting of a parcelable creator,
+    // and the class of the parcelable type for the object.
+    private static final HashMap<ClassLoader, HashMap<String,
+            Pair<Parcelable.Creator<?>, Class<?>>>> sPairedCreators = new HashMap<>();
+
+    /** @hide for internal use only. */
+    static protected final Parcel obtain(int obj) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    static protected final Parcel obtain(long obj) {
+        Parcel res = null;
+        synchronized (sPoolSync) {
+            if (sHolderPool != null) {
+                res = sHolderPool;
+                sHolderPool = res.mPoolNext;
+                res.mPoolNext = null;
+                sHolderPoolSize--;
+            }
+        }
+
+        // When no cache found above, create from scratch; otherwise prepare the
+        // cached object to be used
+        if (res == null) {
+            res = new Parcel(obj);
+        } else {
+            res.mRecycled = false;
+            if (DEBUG_RECYCLE) {
+                res.mStack = new RuntimeException();
+            }
+            res.init(obj);
+        }
+        return res;
+    }
+
+    private Parcel(long nativePtr) {
+        if (DEBUG_RECYCLE) {
+            mStack = new RuntimeException();
+        }
+        //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
+        init(nativePtr);
+    }
+
+    private void init(long nativePtr) {
+        if (nativePtr != 0) {
+            mNativePtr = nativePtr;
+            mOwnsNativeParcelObject = false;
+        } else {
+            mNativePtr = nativeCreate();
+            mOwnsNativeParcelObject = true;
+        }
+    }
+
+    private void freeBuffer() {
+        mFlags = 0;
+        resetSqaushingState();
+        if (mOwnsNativeParcelObject) {
+            nativeFreeBuffer(mNativePtr);
+        }
+        mReadWriteHelper = ReadWriteHelper.DEFAULT;
+    }
+
+    private void destroy() {
+        resetSqaushingState();
+        if (mNativePtr != 0) {
+            if (mOwnsNativeParcelObject) {
+                nativeDestroy(mNativePtr);
+            }
+            mNativePtr = 0;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (DEBUG_RECYCLE) {
+            // we could always have this log on, but it's spammy
+            if (!mRecycled) {
+                Log.w(TAG, "Client did not call Parcel.recycle()", mStack);
+            }
+        }
+        destroy();
+    }
+
+    /**
+     * To be replaced by {@link #readMapInternal(Map, int, ClassLoader, Class, Class)}, but keep
+     * the old API for compatibility usages.
+     */
+    /* package */ void readMapInternal(@NonNull Map outVal, int n,
+            @Nullable ClassLoader loader) {
+        readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
+    }
+
+    @Nullable
+    private <K, V> HashMap<K, V> readHashMapInternal(@Nullable ClassLoader loader,
+            @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        HashMap<K, V> map = new HashMap<>(n);
+        readMapInternal(map, n, loader, clazzKey, clazzValue);
+        return map;
+    }
+
+    private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal,
+            @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+            @Nullable Class<V> clazzValue) {
+        int n = readInt();
+        readMapInternal(outVal, n, loader, clazzKey, clazzValue);
+    }
+
+    private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
+            @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+            @Nullable Class<V> clazzValue) {
+        while (n > 0) {
+            K key = readValue(loader, clazzKey);
+            V value = readValue(loader, clazzValue);
+            outVal.put(key, value);
+            n--;
+        }
+    }
+
+    private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
+            int size, @Nullable ClassLoader loader) {
+        readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
+    }
+
+    /**
+     * Reads a map into {@code map}.
+     *
+     * @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path.
+     * @param lazy   Whether to populate the map with lazy {@link Function} objects for
+     *               length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
+     *               details.
+     * @return a count of the lazy values in the map
+     * @hide
+     */
+    int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+            boolean lazy, @Nullable ClassLoader loader) {
+        int lazyValues = 0;
+        while (size > 0) {
+            String key = readString();
+            Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
+            if (value instanceof LazyValue) {
+                lazyValues++;
+            }
+            if (sorted) {
+                map.append(key, value);
+            } else {
+                map.put(key, value);
+            }
+            size--;
+        }
+        if (sorted) {
+            map.validate();
+        }
+        return lazyValues;
+    }
+
+    /**
+     * @hide For testing only.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
+            @Nullable ClassLoader loader) {
+        final int N = readInt();
+        if (N < 0) {
+            return;
+        }
+        readArrayMapInternal(outVal, N, loader);
+    }
+
+    /**
+     * Reads an array set.
+     *
+     * @param loader The class loader to use.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @Nullable ArraySet<? extends Object> readArraySet(@Nullable ClassLoader loader) {
+        final int size = readInt();
+        if (size < 0) {
+            return null;
+        }
+        ArraySet<Object> result = new ArraySet<>(size);
+        for (int i = 0; i < size; i++) {
+            Object value = readValue(loader);
+            result.append(value);
+        }
+        return result;
+    }
+
+    /**
+     * The method is replaced by {@link #readListInternal(List, int, ClassLoader, Class)}, however
+     * we are keeping this unused method here to allow unsupported app usages.
+     */
+    private void readListInternal(@NonNull List outVal, int n, @Nullable ClassLoader loader) {
+        readListInternal(outVal, n, loader,  /* clazz */ null);
+    }
+
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    private <T> void readListInternal(@NonNull List<? super T> outVal, int n,
+            @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        while (n > 0) {
+            T value = readValue(loader, clazz);
+            //Log.d(TAG, "Unmarshalling value=" + value);
+            outVal.add(value);
+            n--;
+        }
+    }
+
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    @Nullable
+    private <T> ArrayList<T> readArrayListInternal(@Nullable ClassLoader loader,
+            @Nullable Class<? extends T> clazz) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        ArrayList<T> l = new ArrayList<>(n);
+        readListInternal(l, n, loader, clazz);
+        return l;
+    }
+
+    /**
+     * The method is replaced by {@link #readArrayInternal(ClassLoader, Class)}, however
+     * we are keeping this unused method here to allow unsupported app usages.
+     */
+    private void readArrayInternal(@NonNull Object[] outVal, int N,
+            @Nullable ClassLoader loader) {
+        for (int i = 0; i < N; i++) {
+            Object value = readValue(loader, /* clazz */ null);
+            outVal[i] = value;
+        }
+    }
+
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T[] readArrayInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        T[] outVal = (T[]) ((clazz == null) ? new Object[n] : Array.newInstance(clazz, n));
+
+        for (int i = 0; i < n; i++) {
+            T value = readValue(loader, clazz);
+            outVal[i] = value;
+        }
+        return outVal;
+    }
+
+    /**
+     * The method is replaced by {@link #readSparseArray(ClassLoader, Class)}, however
+     * we are keeping this unused method here to allow unsupported app usages.
+     */
+    private void readSparseArrayInternal(@NonNull SparseArray outVal, int N,
+            @Nullable ClassLoader loader) {
+        while (N > 0) {
+            int key = readInt();
+            Object value = readValue(loader);
+            outVal.append(key, value);
+            N--;
+        }
+    }
+
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @Nullable
+    private <T> SparseArray<T> readSparseArrayInternal(@Nullable ClassLoader loader,
+            @Nullable Class<? extends T> clazz) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        SparseArray<T> outVal = new SparseArray<>(n);
+
+        while (n > 0) {
+            int key = readInt();
+            T value = readValue(loader, clazz);
+            outVal.append(key, value);
+            n--;
+        }
+        return outVal;
+    }
+
+
+    private void readSparseBooleanArrayInternal(@NonNull SparseBooleanArray outVal, int N) {
+        while (N > 0) {
+            int key = readInt();
+            boolean value = this.readByte() == 1;
+            //Log.i(TAG, "Unmarshalling key=" + key + " value=" + value);
+            outVal.append(key, value);
+            N--;
+        }
+    }
+
+    private void readSparseIntArrayInternal(@NonNull SparseIntArray outVal, int N) {
+        while (N > 0) {
+            int key = readInt();
+            int value = readInt();
+            outVal.append(key, value);
+            N--;
+        }
+    }
+
+    /**
+     * @hide For testing
+     */
+    public long getOpenAshmemSize() {
+        return nativeGetOpenAshmemSize(mNativePtr);
+    }
+
+    private static String valueTypeToString(int type) {
+        switch (type) {
+            case VAL_NULL: return "VAL_NULL";
+            case VAL_INTEGER: return "VAL_INTEGER";
+            case VAL_MAP: return "VAL_MAP";
+            case VAL_BUNDLE: return "VAL_BUNDLE";
+            case VAL_PERSISTABLEBUNDLE: return "VAL_PERSISTABLEBUNDLE";
+            case VAL_PARCELABLE: return "VAL_PARCELABLE";
+            case VAL_SHORT: return "VAL_SHORT";
+            case VAL_LONG: return "VAL_LONG";
+            case VAL_FLOAT: return "VAL_FLOAT";
+            case VAL_DOUBLE: return "VAL_DOUBLE";
+            case VAL_BOOLEAN: return "VAL_BOOLEAN";
+            case VAL_CHARSEQUENCE: return "VAL_CHARSEQUENCE";
+            case VAL_LIST: return "VAL_LIST";
+            case VAL_SPARSEARRAY: return "VAL_SPARSEARRAY";
+            case VAL_BOOLEANARRAY: return "VAL_BOOLEANARRAY";
+            case VAL_BYTEARRAY: return "VAL_BYTEARRAY";
+            case VAL_STRINGARRAY: return "VAL_STRINGARRAY";
+            case VAL_CHARSEQUENCEARRAY: return "VAL_CHARSEQUENCEARRAY";
+            case VAL_IBINDER: return "VAL_IBINDER";
+            case VAL_PARCELABLEARRAY: return "VAL_PARCELABLEARRAY";
+            case VAL_INTARRAY: return "VAL_INTARRAY";
+            case VAL_LONGARRAY: return "VAL_LONGARRAY";
+            case VAL_BYTE: return "VAL_BYTE";
+            case VAL_SIZE: return "VAL_SIZE";
+            case VAL_SIZEF: return "VAL_SIZEF";
+            case VAL_DOUBLEARRAY: return "VAL_DOUBLEARRAY";
+            case VAL_CHAR: return "VAL_CHAR";
+            case VAL_SHORTARRAY: return "VAL_SHORTARRAY";
+            case VAL_CHARARRAY: return "VAL_CHARARRAY";
+            case VAL_FLOATARRAY: return "VAL_FLOATARRAY";
+            case VAL_OBJECTARRAY: return "VAL_OBJECTARRAY";
+            case VAL_SERIALIZABLE: return "VAL_SERIALIZABLE";
+            default: return "UNKNOWN(" + type + ")";
+        }
+    }
+}
diff --git a/android-34/android/os/ParcelArrayPerfTest.java b/android-34/android/os/ParcelArrayPerfTest.java
new file mode 100644
index 0000000..af6d6b0
--- /dev/null
+++ b/android-34/android/os/ParcelArrayPerfTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ParcelArrayPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameters(name = "size={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] { {1}, {10}, {100}, {1000} });
+    }
+
+    private final int mSize;
+
+    private Parcel mWriteParcel;
+
+    private byte[] mByteArray;
+    private int[] mIntArray;
+    private long[] mLongArray;
+
+    private Parcel mByteParcel;
+    private Parcel mIntParcel;
+    private Parcel mLongParcel;
+
+    public ParcelArrayPerfTest(int size) {
+        mSize = size;
+    }
+
+    @Before
+    public void setUp() {
+        mWriteParcel = Parcel.obtain();
+
+        mByteArray = new byte[mSize];
+        mIntArray = new int[mSize];
+        mLongArray = new long[mSize];
+
+        mByteParcel = Parcel.obtain();
+        mByteParcel.writeByteArray(mByteArray);
+        mIntParcel = Parcel.obtain();
+        mIntParcel.writeIntArray(mIntArray);
+        mLongParcel = Parcel.obtain();
+        mLongParcel.writeLongArray(mLongArray);
+    }
+
+    @After
+    public void tearDown() {
+        mWriteParcel.recycle();
+        mWriteParcel = null;
+    }
+
+    @Test
+    public void timeWriteByteArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mWriteParcel.setDataPosition(0);
+            mWriteParcel.writeByteArray(mByteArray);
+        }
+    }
+
+    @Test
+    public void timeCreateByteArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mByteParcel.setDataPosition(0);
+            mByteParcel.createByteArray();
+        }
+    }
+
+    @Test
+    public void timeReadByteArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mByteParcel.setDataPosition(0);
+            mByteParcel.readByteArray(mByteArray);
+        }
+    }
+
+    @Test
+    public void timeWriteIntArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mWriteParcel.setDataPosition(0);
+            mWriteParcel.writeIntArray(mIntArray);
+        }
+    }
+
+    @Test
+    public void timeCreateIntArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mIntParcel.setDataPosition(0);
+            mIntParcel.createIntArray();
+        }
+    }
+
+    @Test
+    public void timeReadIntArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mIntParcel.setDataPosition(0);
+            mIntParcel.readIntArray(mIntArray);
+        }
+    }
+
+    @Test
+    public void timeWriteLongArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mWriteParcel.setDataPosition(0);
+            mWriteParcel.writeLongArray(mLongArray);
+        }
+    }
+
+    @Test
+    public void timeCreateLongArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mLongParcel.setDataPosition(0);
+            mLongParcel.createLongArray();
+        }
+    }
+
+    @Test
+    public void timeReadLongArray() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mLongParcel.setDataPosition(0);
+            mLongParcel.readLongArray(mLongArray);
+        }
+    }
+}
diff --git a/android-34/android/os/ParcelDuration.java b/android-34/android/os/ParcelDuration.java
new file mode 100644
index 0000000..37cde31
--- /dev/null
+++ b/android-34/android/os/ParcelDuration.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+
+import java.time.Duration;
+
+/**
+ * Parcelable version of {@link Duration} that can be used in binder calls.
+ *
+ * @hide
+ */
+public final class ParcelDuration implements Parcelable {
+
+    private final long mSeconds;
+    private final int mNanos;
+
+    /**
+     * Construct a Duration object using the given millisecond value.
+     *
+     * @hide
+     */
+    public ParcelDuration(long ms) {
+        this(Duration.ofMillis(ms));
+    }
+
+    /**
+     * Wrap a {@link Duration} instance.
+     *
+     * @param duration The {@link Duration} instance to wrap.
+     */
+    public ParcelDuration(@NonNull Duration duration) {
+        mSeconds = duration.getSeconds();
+        mNanos = duration.getNano();
+    }
+
+    private ParcelDuration(@NonNull Parcel parcel) {
+        mSeconds = parcel.readLong();
+        mNanos = parcel.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeLong(mSeconds);
+        parcel.writeInt(mNanos);
+    }
+
+    /**
+     * Returns a {@link Duration} instance that's equivalent to this Duration's length.
+     *
+     * @return a {@link Duration} instance of identical length.
+     */
+    @NonNull
+    public Duration getDuration() {
+        return Duration.ofSeconds(mSeconds, mNanos);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return getDuration().toString();
+    }
+
+    /**
+     * Creator for Duration.
+     */
+    @NonNull
+    public static final Parcelable.Creator<ParcelDuration> CREATOR =
+            new Parcelable.Creator<ParcelDuration>() {
+
+        @Override
+        @NonNull
+        public ParcelDuration createFromParcel(@NonNull Parcel source) {
+            return new ParcelDuration(source);
+        }
+
+        @Override
+        @NonNull
+        public ParcelDuration[] newArray(int size) {
+            return new ParcelDuration[size];
+        }
+    };
+}
diff --git a/android-34/android/os/ParcelFileDescriptor.java b/android-34/android/os/ParcelFileDescriptor.java
new file mode 100644
index 0000000..93d5082
--- /dev/null
+++ b/android-34/android/os/ParcelFileDescriptor.java
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.F_DUPFD;
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+import static android.system.OsConstants.O_CLOEXEC;
+import static android.system.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SOCK_CLOEXEC;
+import static android.system.OsConstants.SOCK_SEQPACKET;
+import static android.system.OsConstants.SOCK_STREAM;
+import static android.system.OsConstants.S_IROTH;
+import static android.system.OsConstants.S_IRWXG;
+import static android.system.OsConstants.S_IRWXU;
+import static android.system.OsConstants.S_ISLNK;
+import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.S_IWOTH;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.MessageQueue.OnFileDescriptorEventListener;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+import android.util.Log;
+import android.util.Slog;
+
+import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+import libcore.io.Memory;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.UncheckedIOException;
+import java.net.DatagramSocket;
+import java.net.Socket;
+import java.nio.ByteOrder;
+
+/**
+ * The FileDescriptor returned by {@link Parcel#readFileDescriptor}, allowing
+ * you to close it when done with it.
+ */
+public class ParcelFileDescriptor implements Parcelable, Closeable {
+    private static final String TAG = "ParcelFileDescriptor";
+
+    private final FileDescriptor mFd;
+
+    /**
+     * Optional socket used to communicate close events, status at close, and
+     * detect remote process crashes.
+     */
+    private FileDescriptor mCommFd;
+
+    /**
+     * Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid
+     * double-closing {@link #mFd}.
+     * mClosed is always true if mWrapped is non-null.
+     */
+    private final ParcelFileDescriptor mWrapped;
+
+    /**
+     * Maximum {@link #mStatusBuf} size; longer status messages will be
+     * truncated.
+     */
+    private static final int MAX_STATUS = 1024;
+
+    /**
+     * Temporary buffer used by {@link #readCommStatus(FileDescriptor, byte[])},
+     * allocated on-demand.
+     */
+    private byte[] mStatusBuf;
+
+    /**
+     * Status read by {@link #checkError()}, or null if not read yet.
+     */
+    private Status mStatus;
+
+    private volatile boolean mClosed;
+
+    private final CloseGuard mGuard = CloseGuard.get();
+
+    /**
+     * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
+     * this file doesn't already exist, then create the file with permissions
+     * such that any application can read it.
+     *
+     * @deprecated Creating world-readable files is very dangerous, and likely
+     *             to cause security holes in applications. It is strongly
+     *             discouraged; instead, applications should use more formal
+     *             mechanism for interactions such as {@link ContentProvider},
+     *             {@link BroadcastReceiver}, and {@link android.app.Service}.
+     *             There are no guarantees that this access mode will remain on
+     *             a file, such as when it goes through a backup and restore.
+     */
+    @Deprecated
+    public static final int MODE_WORLD_READABLE = 0x00000001;
+
+    /**
+     * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
+     * this file doesn't already exist, then create the file with permissions
+     * such that any application can write it.
+     *
+     * @deprecated Creating world-writable files is very dangerous, and likely
+     *             to cause security holes in applications. It is strongly
+     *             discouraged; instead, applications should use more formal
+     *             mechanism for interactions such as {@link ContentProvider},
+     *             {@link BroadcastReceiver}, and {@link android.app.Service}.
+     *             There are no guarantees that this access mode will remain on
+     *             a file, such as when it goes through a backup and restore.
+     */
+    @Deprecated
+    public static final int MODE_WORLD_WRITEABLE = 0x00000002;
+
+    /**
+     * For use with {@link #open}: open the file with read-only access.
+     */
+    public static final int MODE_READ_ONLY = 0x10000000;
+
+    /**
+     * For use with {@link #open}: open the file with write-only access.
+     */
+    public static final int MODE_WRITE_ONLY = 0x20000000;
+
+    /**
+     * For use with {@link #open}: open the file with read and write access.
+     */
+    public static final int MODE_READ_WRITE = 0x30000000;
+
+    /**
+     * For use with {@link #open}: create the file if it doesn't already exist.
+     */
+    public static final int MODE_CREATE = 0x08000000;
+
+    /**
+     * For use with {@link #open}: erase contents of file when opening.
+     */
+    public static final int MODE_TRUNCATE = 0x04000000;
+
+    /**
+     * For use with {@link #open}: append to end of file while writing.
+     */
+    public static final int MODE_APPEND = 0x02000000;
+
+    /**
+     * Create a new ParcelFileDescriptor wrapped around another descriptor. By
+     * default all method calls are delegated to the wrapped descriptor.
+     */
+    public ParcelFileDescriptor(ParcelFileDescriptor wrapped) {
+        // We keep a strong reference to the wrapped PFD, and rely on its
+        // finalizer to trigger CloseGuard. All calls are delegated to wrapper.
+        mWrapped = wrapped;
+        mFd = null;
+        mCommFd = null;
+        mClosed = true;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public ParcelFileDescriptor(FileDescriptor fd) {
+        this(fd, null);
+    }
+
+    /** {@hide} */
+    public ParcelFileDescriptor(FileDescriptor fd, FileDescriptor commChannel) {
+        if (fd == null) {
+            throw new NullPointerException("FileDescriptor must not be null");
+        }
+        mWrapped = null;
+        mFd = fd;
+        IoUtils.setFdOwner(mFd, this);
+
+        mCommFd = commChannel;
+        if (mCommFd != null) {
+            IoUtils.setFdOwner(mCommFd, this);
+        }
+
+        mGuard.open("close");
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor accessing a given file.
+     * <p>
+     * This method should only be used for files that you have direct access to;
+     * if you'd like to work with files hosted outside your app, use an API like
+     * {@link ContentResolver#openFile(Uri, String, CancellationSignal)}.
+     *
+     * @param file The file to be opened.
+     * @param mode The desired access mode, must be one of
+     *            {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
+     *            {@link #MODE_READ_WRITE}; may also be any combination of
+     *            {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
+     *            {@link #MODE_WORLD_READABLE}, and
+     *            {@link #MODE_WORLD_WRITEABLE}.
+     * @return a new ParcelFileDescriptor pointing to the given file.
+     * @throws FileNotFoundException if the given file does not exist or can not
+     *             be opened with the requested mode.
+     * @see #parseMode(String)
+     */
+    public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
+        final FileDescriptor fd = openInternal(file, mode);
+        if (fd == null) return null;
+
+        return new ParcelFileDescriptor(fd);
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor accessing a given file.
+     * <p>
+     * This method should only be used for files that you have direct access to;
+     * if you'd like to work with files hosted outside your app, use an API like
+     * {@link ContentResolver#openFile(Uri, String, CancellationSignal)}.
+     *
+     * @param file The file to be opened.
+     * @param mode The desired access mode, must be one of
+     *            {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
+     *            {@link #MODE_READ_WRITE}; may also be any combination of
+     *            {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
+     *            {@link #MODE_WORLD_READABLE}, and
+     *            {@link #MODE_WORLD_WRITEABLE}.
+     * @param handler to call listener from; must not be null.
+     * @param listener to be invoked when the returned descriptor has been
+     *            closed; must not be null.
+     * @return a new ParcelFileDescriptor pointing to the given file.
+     * @throws FileNotFoundException if the given file does not exist or can not
+     *             be opened with the requested mode.
+     * @see #parseMode(String)
+     */
+    // We can't accept a generic Executor here, since we need to use
+    // MessageQueue.addOnFileDescriptorEventListener()
+    @SuppressLint("ExecutorRegistration")
+    public static ParcelFileDescriptor open(File file, int mode, Handler handler,
+            final OnCloseListener listener) throws IOException {
+        if (handler == null) {
+            throw new IllegalArgumentException("Handler must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("Listener must not be null");
+        }
+
+        final FileDescriptor fd = openInternal(file, mode);
+        if (fd == null) return null;
+
+        return fromFd(fd, handler, listener);
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor wrapping an already-opened file.
+     *
+     * @param pfd The already-opened file.
+     * @param handler to call listener from.
+     * @param listener to be invoked when the returned descriptor has been
+     *            closed.
+     * @return a new ParcelFileDescriptor pointing to the given file.
+     */
+    // We can't accept a generic Executor here, since we need to use
+    // MessageQueue.addOnFileDescriptorEventListener()
+    @SuppressLint("ExecutorRegistration")
+    public static @NonNull ParcelFileDescriptor wrap(@NonNull ParcelFileDescriptor pfd,
+            @NonNull Handler handler, @NonNull OnCloseListener listener) throws IOException {
+        final FileDescriptor original = new FileDescriptor();
+        original.setInt$(pfd.detachFd());
+        return fromFd(original, handler, listener);
+    }
+
+    /** {@hide} */
+    public static ParcelFileDescriptor fromFd(FileDescriptor fd, Handler handler,
+            final OnCloseListener listener) throws IOException {
+        if (handler == null) {
+            throw new IllegalArgumentException("Handler must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("Listener must not be null");
+        }
+
+        final FileDescriptor[] comm = createCommSocketPair();
+        final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]);
+        final MessageQueue queue = handler.getLooper().getQueue();
+        queue.addOnFileDescriptorEventListener(comm[1],
+                OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
+            @Override
+            public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+                Status status = null;
+                if ((events & OnFileDescriptorEventListener.EVENT_INPUT) != 0) {
+                    final byte[] buf = new byte[MAX_STATUS];
+                    status = readCommStatus(fd, buf);
+                } else if ((events & OnFileDescriptorEventListener.EVENT_ERROR) != 0) {
+                    status = new Status(Status.DEAD);
+                }
+                if (status != null) {
+                    queue.removeOnFileDescriptorEventListener(fd);
+                    IoUtils.closeQuietly(fd);
+                    listener.onClose(status.asIOException());
+                    return 0;
+                }
+                return EVENT_INPUT;
+            }
+        });
+
+        return pfd;
+    }
+
+    private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
+        if ((mode & MODE_WRITE_ONLY) != 0 && (mode & MODE_APPEND) == 0
+                && (mode & MODE_TRUNCATE) == 0 && ((mode & MODE_READ_ONLY) == 0)
+                && file != null && file.exists()) {
+            Slog.wtfQuiet(TAG, "ParcelFileDescriptor.open is called with w without t or a or r, "
+                    + "which will have a different behavior beginning in Android Q."
+                    + "\nMode: " + mode + "\nFilename: " + file.getPath());
+        }
+
+        final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
+
+        int realMode = S_IRWXU | S_IRWXG;
+        if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
+        if ((mode & MODE_WORLD_WRITEABLE) != 0) realMode |= S_IWOTH;
+
+        final String path = file.getPath();
+        try {
+            return Os.open(path, flags, realMode);
+        } catch (ErrnoException e) {
+            throw new FileNotFoundException(e.getMessage());
+        }
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor that is a dup of an existing
+     * FileDescriptor.  This obeys standard POSIX semantics, where the
+     * new file descriptor shared state such as file position with the
+     * original file descriptor.
+     */
+    public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
+        try {
+            final FileDescriptor fd = new FileDescriptor();
+            int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+            fd.setInt$(intfd);
+            return new ParcelFileDescriptor(fd);
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor that is a dup of the existing
+     * FileDescriptor.  This obeys standard POSIX semantics, where the
+     * new file descriptor shared state such as file position with the
+     * original file descriptor.
+     */
+    public ParcelFileDescriptor dup() throws IOException {
+        if (mWrapped != null) {
+            return mWrapped.dup();
+        } else {
+            return dup(getFileDescriptor());
+        }
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor from a raw native fd.  The new
+     * ParcelFileDescriptor holds a dup of the original fd passed in here,
+     * so you must still close that fd as well as the new ParcelFileDescriptor.
+     *
+     * @param fd The native fd that the ParcelFileDescriptor should dup.
+     *
+     * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+     * for a dup of the given fd.
+     */
+    public static ParcelFileDescriptor fromFd(int fd) throws IOException {
+        final FileDescriptor original = new FileDescriptor();
+        original.setInt$(fd);
+
+        try {
+            final FileDescriptor dup = new FileDescriptor();
+            int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+            dup.setInt$(intfd);
+            return new ParcelFileDescriptor(dup);
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Take ownership of a raw native fd in to a new ParcelFileDescriptor.
+     * The returned ParcelFileDescriptor now owns the given fd, and will be
+     * responsible for closing it.
+     * <p>
+     * <strong>WARNING:</strong> You must not close the fd yourself after
+     * this call, and ownership of the file descriptor must have been
+     * released prior to the call to this function.
+     *
+     * @param fd The native fd that the ParcelFileDescriptor should adopt.
+     *
+     * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+     * for the given fd.
+     */
+    public static ParcelFileDescriptor adoptFd(int fd) {
+        final FileDescriptor fdesc = new FileDescriptor();
+        fdesc.setInt$(fd);
+
+        return new ParcelFileDescriptor(fdesc);
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor from the specified Socket.  The new
+     * ParcelFileDescriptor holds a dup of the original FileDescriptor in
+     * the Socket, so you must still close the Socket as well as the new
+     * ParcelFileDescriptor.
+     * <p>
+     * <strong>WARNING:</strong> Prior to API level 29, this function would not
+     * actually dup the Socket's FileDescriptor, and would take a
+     * reference to the its internal FileDescriptor instead. If the Socket
+     * gets garbage collected before the ParcelFileDescriptor, this may
+     * lead to the ParcelFileDescriptor being unexpectedly closed. To avoid
+     * this, the following pattern can be used:
+     * <pre>{@code
+     *    ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket).dup();
+     * }</pre>
+     *
+     * @param socket The Socket whose FileDescriptor is used to create
+     *               a new ParcelFileDescriptor.
+     *
+     * @return A new ParcelFileDescriptor with a duped copy of the
+     * FileDescriptor of the specified Socket.
+     *
+     * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
+     */
+    public static ParcelFileDescriptor fromSocket(Socket socket) {
+        FileDescriptor fd = socket.getFileDescriptor$();
+        try {
+            return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    /**
+     * Create a new ParcelFileDescriptor from the specified DatagramSocket. The
+     * new ParcelFileDescriptor holds a dup of the original FileDescriptor in
+     * the DatagramSocket, so you must still close the DatagramSocket as well
+     * as the new ParcelFileDescriptor.
+     * <p>
+     * <strong>WARNING:</strong> Prior to API level 29, this function would not
+     * actually dup the DatagramSocket's FileDescriptor, and would take a
+     * reference to the its internal FileDescriptor instead. If the DatagramSocket
+     * gets garbage collected before the ParcelFileDescriptor, this may
+     * lead to the ParcelFileDescriptor being unexpectedly closed. To avoid
+     * this, the following pattern can be used:
+     * <pre>{@code
+     *    ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket).dup();
+     * }</pre>
+     *
+     * @param datagramSocket The DatagramSocket whose FileDescriptor is used
+     *               to create a new ParcelFileDescriptor.
+     *
+     * @return A new ParcelFileDescriptor with a duped copy of the
+     * FileDescriptor of the specified Socket.
+     *
+     * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
+     */
+    public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
+        FileDescriptor fd = datagramSocket.getFileDescriptor$();
+        try {
+            return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    /**
+     * Create two ParcelFileDescriptors structured as a data pipe.  The first
+     * ParcelFileDescriptor in the returned array is the read side; the second
+     * is the write side.
+     */
+    public static ParcelFileDescriptor[] createPipe() throws IOException {
+        try {
+            final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
+            return new ParcelFileDescriptor[] {
+                    new ParcelFileDescriptor(fds[0]),
+                    new ParcelFileDescriptor(fds[1]) };
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Create two ParcelFileDescriptors structured as a data pipe. The first
+     * ParcelFileDescriptor in the returned array is the read side; the second
+     * is the write side.
+     * <p>
+     * The write end has the ability to deliver an error message through
+     * {@link #closeWithError(String)} which can be handled by the read end
+     * calling {@link #checkError()}, usually after detecting an EOF.
+     * This can also be used to detect remote crashes.
+     */
+    public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
+        try {
+            final FileDescriptor[] comm = createCommSocketPair();
+            final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
+            return new ParcelFileDescriptor[] {
+                    new ParcelFileDescriptor(fds[0], comm[0]),
+                    new ParcelFileDescriptor(fds[1], comm[1]) };
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Create two ParcelFileDescriptors structured as a pair of sockets
+     * connected to each other. The two sockets are indistinguishable.
+     */
+    public static ParcelFileDescriptor[] createSocketPair() throws IOException {
+        return createSocketPair(SOCK_STREAM);
+    }
+
+    /**
+     * @hide
+     */
+    public static ParcelFileDescriptor[] createSocketPair(int type) throws IOException {
+        try {
+            final FileDescriptor fd0 = new FileDescriptor();
+            final FileDescriptor fd1 = new FileDescriptor();
+            Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
+            return new ParcelFileDescriptor[] {
+                    new ParcelFileDescriptor(fd0),
+                    new ParcelFileDescriptor(fd1) };
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Create two ParcelFileDescriptors structured as a pair of sockets
+     * connected to each other. The two sockets are indistinguishable.
+     * <p>
+     * Both ends have the ability to deliver an error message through
+     * {@link #closeWithError(String)} which can be detected by the other end
+     * calling {@link #checkError()}, usually after detecting an EOF.
+     * This can also be used to detect remote crashes.
+     */
+    public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException {
+        return createReliableSocketPair(SOCK_STREAM);
+    }
+
+    /**
+     * @hide
+     */
+    public static ParcelFileDescriptor[] createReliableSocketPair(int type) throws IOException {
+        try {
+            final FileDescriptor[] comm = createCommSocketPair();
+            final FileDescriptor fd0 = new FileDescriptor();
+            final FileDescriptor fd1 = new FileDescriptor();
+            Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
+            return new ParcelFileDescriptor[] {
+                    new ParcelFileDescriptor(fd0, comm[0]),
+                    new ParcelFileDescriptor(fd1, comm[1]) };
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    private static FileDescriptor[] createCommSocketPair() throws IOException {
+        try {
+            // Use SOCK_SEQPACKET so that we have a guarantee that the status
+            // is written and read atomically as one unit and is not split
+            // across multiple IO operations.
+            final FileDescriptor comm1 = new FileDescriptor();
+            final FileDescriptor comm2 = new FileDescriptor();
+            Os.socketpair(AF_UNIX, SOCK_SEQPACKET | ifAtLeastQ(SOCK_CLOEXEC), 0, comm1, comm2);
+            IoUtils.setBlocking(comm1, false);
+            IoUtils.setBlocking(comm2, false);
+            return new FileDescriptor[] { comm1, comm2 };
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * @hide Please use createPipe() or ContentProvider.openPipeHelper().
+     * Gets a file descriptor for a read-only copy of the given data.
+     *
+     * @param data Data to copy.
+     * @param name Name for the shared memory area that may back the file descriptor.
+     *        This is purely informative and may be {@code null}.
+     * @return A ParcelFileDescriptor.
+     * @throws IOException if there is an error while creating the shared memory area.
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
+        if (data == null) return null;
+        MemoryFile file = new MemoryFile(name, data.length);
+        try {
+            if (data.length > 0) {
+                file.writeBytes(data, 0, 0, data.length);
+            }
+            file.deactivate();
+            FileDescriptor fd = file.getFileDescriptor();
+            return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+        } finally {
+            file.close();
+        }
+    }
+
+    /**
+     * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
+     * with {@link #open}.
+     * <p>
+     * The argument must define at least one of the following base access modes:
+     * <ul>
+     * <li>"r" indicates the file should be opened in read-only mode, equivalent
+     * to {@link OsConstants#O_RDONLY}.
+     * <li>"w" indicates the file should be opened in write-only mode,
+     * equivalent to {@link OsConstants#O_WRONLY}.
+     * <li>"rw" indicates the file should be opened in read-write mode,
+     * equivalent to {@link OsConstants#O_RDWR}.
+     * </ul>
+     * In addition to a base access mode, the following additional modes may
+     * requested:
+     * <ul>
+     * <li>"a" indicates the file should be opened in append mode, equivalent to
+     * {@link OsConstants#O_APPEND}. Before each write, the file offset is
+     * positioned at the end of the file.
+     * <li>"t" indicates the file should be opened in truncate mode, equivalent
+     * to {@link OsConstants#O_TRUNC}. If the file already exists and is a
+     * regular file and is opened for writing, it will be truncated to length 0.
+     * </ul>
+     *
+     * @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
+     *             or "rwt".
+     * @return A bitmask representing the given file mode.
+     * @throws IllegalArgumentException if the given string does not match a known file mode.
+     */
+    public static int parseMode(String mode) {
+        return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
+    }
+
+    /**
+     * Return the filesystem path of the real file on disk that is represented
+     * by the given {@link FileDescriptor}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static File getFile(FileDescriptor fd) throws IOException {
+        try {
+            final String path = Os.readlink("/proc/self/fd/" + fd.getInt$());
+            if (OsConstants.S_ISREG(Os.stat(path).st_mode)
+                    || OsConstants.S_ISCHR(Os.stat(path).st_mode)) {
+                return new File(path);
+            } else {
+                throw new IOException("Not a regular file or character device: " + path);
+            }
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Retrieve the actual FileDescriptor associated with this object.
+     *
+     * @return Returns the FileDescriptor associated with this object.
+     */
+    public FileDescriptor getFileDescriptor() {
+        if (mWrapped != null) {
+            return mWrapped.getFileDescriptor();
+        } else {
+            return mFd;
+        }
+    }
+
+    /**
+     * Return the total size of the file representing this fd, as determined by
+     * {@code stat()}. Returns -1 if the fd is not a file.
+     */
+    public long getStatSize() {
+        if (mWrapped != null) {
+            return mWrapped.getStatSize();
+        } else {
+            try {
+                final StructStat st = Os.fstat(mFd);
+                if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
+                    return st.st_size;
+                } else {
+                    return -1;
+                }
+            } catch (ErrnoException e) {
+                Log.w(TAG, "fstat() failed: " + e);
+                return -1;
+            }
+        }
+    }
+
+    /**
+     * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream,
+     * and I really don't think we want it to be public.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public long seekTo(long pos) throws IOException {
+        if (mWrapped != null) {
+            return mWrapped.seekTo(pos);
+        } else {
+            try {
+                return Os.lseek(mFd, pos, SEEK_SET);
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        }
+    }
+
+    /**
+     * Return the native fd int for this ParcelFileDescriptor.  The
+     * ParcelFileDescriptor still owns the fd, and it still must be closed
+     * through this API.
+     * <p>
+     * <strong>WARNING:</strong> Do not call close on the return value of this
+     * function or pass it to a function that assumes ownership of the fd.
+     */
+    public int getFd() {
+        if (mWrapped != null) {
+            return mWrapped.getFd();
+        } else {
+            if (mClosed) {
+                throw new IllegalStateException("Already closed");
+            }
+            return mFd.getInt$();
+        }
+    }
+
+    /**
+     * Return the native fd int for this ParcelFileDescriptor and detach it from
+     * the object here. You are now responsible for closing the fd in native
+     * code.
+     * <p>
+     * You should not detach when the original creator of the descriptor is
+     * expecting a reliable signal through {@link #close()} or
+     * {@link #closeWithError(String)}.
+     *
+     * @see #canDetectErrors()
+     */
+    public int detachFd() {
+        if (mWrapped != null) {
+            return mWrapped.detachFd();
+        } else {
+            if (mClosed) {
+                throw new IllegalStateException("Already closed");
+            }
+            int fd = IoUtils.acquireRawFd(mFd);
+            writeCommStatusAndClose(Status.DETACHED, null);
+            mClosed = true;
+            mGuard.close();
+            releaseResources();
+            return fd;
+        }
+    }
+
+    /**
+     * Close the ParcelFileDescriptor. This implementation closes the underlying
+     * OS resources allocated to represent this stream.
+     *
+     * @throws IOException
+     *             If an error occurs attempting to close this ParcelFileDescriptor.
+     */
+    @Override
+    public void close() throws IOException {
+        if (mWrapped != null) {
+            try {
+                mWrapped.close();
+            } finally {
+                releaseResources();
+            }
+        } else {
+            closeWithStatus(Status.OK, null);
+        }
+    }
+
+    /**
+     * Close the ParcelFileDescriptor, informing any peer that an error occurred
+     * while processing. If the creator of this descriptor is not observing
+     * errors, it will close normally.
+     *
+     * @param msg describing the error; must not be null.
+     */
+    public void closeWithError(String msg) throws IOException {
+        if (mWrapped != null) {
+            try {
+                mWrapped.closeWithError(msg);
+            } finally {
+                releaseResources();
+            }
+        } else {
+            if (msg == null) {
+                throw new IllegalArgumentException("Message must not be null");
+            }
+            closeWithStatus(Status.ERROR, msg);
+        }
+    }
+
+    private void closeWithStatus(int status, String msg) {
+        if (mClosed) return;
+        mClosed = true;
+        if (mGuard != null) {
+            mGuard.close();
+        }
+        // Status MUST be sent before closing actual descriptor
+        writeCommStatusAndClose(status, msg);
+        IoUtils.closeQuietly(mFd);
+        releaseResources();
+    }
+
+    /**
+     * Called when the fd is being closed, for subclasses to release any other resources
+     * associated with it, such as acquired providers.
+     * @hide
+     */
+    public void releaseResources() {
+    }
+
+    private byte[] getOrCreateStatusBuffer() {
+        if (mStatusBuf == null) {
+            mStatusBuf = new byte[MAX_STATUS];
+        }
+        return mStatusBuf;
+    }
+
+    private void writeCommStatusAndClose(int status, String msg) {
+        if (mCommFd == null) {
+            // Not reliable, or someone already sent status
+            if (msg != null) {
+                Log.w(TAG, "Unable to inform peer: " + msg);
+            }
+            return;
+        }
+
+        if (status == Status.DETACHED) {
+            Log.w(TAG, "Peer expected signal when closed; unable to deliver after detach");
+        }
+
+        try {
+            if (status == Status.SILENCE) return;
+
+            // Since we're about to close, read off any remote status. It's
+            // okay to remember missing here.
+            mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer());
+
+            // Skip writing status when other end has already gone away.
+            if (mStatus != null) return;
+
+            try {
+                final byte[] buf = getOrCreateStatusBuffer();
+                int writePtr = 0;
+
+                Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN);
+                writePtr += 4;
+
+                if (msg != null) {
+                    final byte[] rawMsg = msg.getBytes();
+                    final int len = Math.min(rawMsg.length, buf.length - writePtr);
+                    System.arraycopy(rawMsg, 0, buf, writePtr, len);
+                    writePtr += len;
+                }
+
+                // Must write the entire status as a single operation.
+                Os.write(mCommFd, buf, 0, writePtr);
+            } catch (ErrnoException e) {
+                // Reporting status is best-effort
+                Log.w(TAG, "Failed to report status: " + e);
+            } catch (InterruptedIOException e) {
+                // Reporting status is best-effort
+                Log.w(TAG, "Failed to report status: " + e);
+            }
+
+        } finally {
+            IoUtils.closeQuietly(mCommFd);
+            mCommFd = null;
+        }
+    }
+
+    private static Status readCommStatus(FileDescriptor comm, byte[] buf) {
+        try {
+            // Must read the entire status as a single operation.
+            final int n = Os.read(comm, buf, 0, buf.length);
+            if (n == 0) {
+                // EOF means they're dead
+                return new Status(Status.DEAD);
+            } else {
+                final int status = Memory.peekInt(buf, 0, ByteOrder.BIG_ENDIAN);
+                if (status == Status.ERROR) {
+                    final String msg = new String(buf, 4, n - 4);
+                    return new Status(status, msg);
+                }
+                return new Status(status);
+            }
+        } catch (ErrnoException e) {
+            if (e.errno == OsConstants.EAGAIN) {
+                // Remote is still alive, but no status written yet
+                return null;
+            } else {
+                Log.d(TAG, "Failed to read status; assuming dead: " + e);
+                return new Status(Status.DEAD);
+            }
+        } catch (InterruptedIOException e) {
+            Log.d(TAG, "Failed to read status; assuming dead: " + e);
+            return new Status(Status.DEAD);
+        }
+    }
+
+    /**
+     * Indicates if this ParcelFileDescriptor can communicate and detect remote
+     * errors/crashes.
+     *
+     * @see #checkError()
+     */
+    public boolean canDetectErrors() {
+        if (mWrapped != null) {
+            return mWrapped.canDetectErrors();
+        } else {
+            return mCommFd != null;
+        }
+    }
+
+    /**
+     * Detect and throw if the other end of a pipe or socket pair encountered an
+     * error or crashed. This allows a reader to distinguish between a valid EOF
+     * and an error/crash.
+     * <p>
+     * If this ParcelFileDescriptor is unable to detect remote errors, it will
+     * return silently.
+     *
+     * @throws IOException for normal errors.
+     * @throws FileDescriptorDetachedException
+     *            if the remote side called {@link #detachFd()}. Once detached, the remote
+     *            side is unable to communicate any errors through
+     *            {@link #closeWithError(String)}.
+     * @see #canDetectErrors()
+     */
+    public void checkError() throws IOException {
+        if (mWrapped != null) {
+            mWrapped.checkError();
+        } else {
+            if (mStatus == null) {
+                if (mCommFd == null) {
+                    Log.w(TAG, "Peer didn't provide a comm channel; unable to check for errors");
+                    return;
+                }
+
+                // Try reading status; it might be null if nothing written yet.
+                // Either way, we keep comm open to write our status later.
+                mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer());
+            }
+
+            if (mStatus == null || mStatus.status == Status.OK) {
+                // No status yet, or everything is peachy!
+                return;
+            } else {
+                throw mStatus.asIOException();
+            }
+        }
+    }
+
+    /**
+     * An InputStream you can create on a ParcelFileDescriptor, which will
+     * take care of calling {@link ParcelFileDescriptor#close
+     * ParcelFileDescriptor.close()} for you when the stream is closed.
+     */
+    public static class AutoCloseInputStream extends FileInputStream {
+        private final ParcelFileDescriptor mPfd;
+
+        public AutoCloseInputStream(ParcelFileDescriptor pfd) {
+            super(pfd.getFileDescriptor());
+            mPfd = pfd;
+        }
+
+        @Override
+        public void close() throws IOException {
+            try {
+                super.close();
+            } finally {
+                mPfd.close();
+            }
+        }
+
+        @Override
+        public int read() throws IOException {
+            final int result = super.read();
+            if (result == -1 && mPfd.canDetectErrors()) {
+                // Check for errors only on EOF, to minimize overhead.
+                mPfd.checkError();
+            }
+            return result;
+        }
+
+        @Override
+        public int read(byte[] b) throws IOException {
+            final int result = super.read(b);
+            if (result == -1 && mPfd.canDetectErrors()) {
+                mPfd.checkError();
+            }
+            return result;
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
+            final int result = super.read(b, off, len);
+            if (result == -1 && mPfd.canDetectErrors()) {
+                mPfd.checkError();
+            }
+            return result;
+        }
+    }
+
+    /**
+     * An OutputStream you can create on a ParcelFileDescriptor, which will
+     * take care of calling {@link ParcelFileDescriptor#close
+     * ParcelFileDescriptor.close()} for you when the stream is closed.
+     */
+    public static class AutoCloseOutputStream extends FileOutputStream {
+        private final ParcelFileDescriptor mPfd;
+
+        public AutoCloseOutputStream(ParcelFileDescriptor pfd) {
+            super(pfd.getFileDescriptor());
+            mPfd = pfd;
+        }
+
+        @Override
+        public void close() throws IOException {
+            try {
+                super.close();
+            } finally {
+                mPfd.close();
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (mWrapped != null) {
+            return mWrapped.toString();
+        } else {
+            return "{ParcelFileDescriptor: " + mFd + "}";
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (mWrapped != null) {
+            releaseResources();
+        }
+        if (mGuard != null) {
+            mGuard.warnIfOpen();
+        }
+        try {
+            if (!mClosed) {
+                // mWrapped was and is null.
+                closeWithStatus(Status.LEAKED, null);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        if (mWrapped != null) {
+            return mWrapped.describeContents();
+        } else {
+            return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags,
+     * the file descriptor will be closed after a copy is written to the Parcel.
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        if (mWrapped != null) {
+            try {
+                mWrapped.writeToParcel(out, flags);
+            } finally {
+                releaseResources();
+            }
+        } else {
+            if (mCommFd != null) {
+                out.writeInt(1);
+                out.writeFileDescriptor(mFd);
+                out.writeFileDescriptor(mCommFd);
+            } else {
+                out.writeInt(0);
+                out.writeFileDescriptor(mFd);
+            }
+            if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
+                // Not a real close, so emit no status
+                closeWithStatus(Status.SILENCE, null);
+            }
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ParcelFileDescriptor> CREATOR
+            = new Parcelable.Creator<ParcelFileDescriptor>() {
+        @Override
+        public ParcelFileDescriptor createFromParcel(Parcel in) {
+            int hasCommChannel = in.readInt();
+            final FileDescriptor fd = in.readRawFileDescriptor();
+            FileDescriptor commChannel = null;
+            if (hasCommChannel != 0) {
+                commChannel = in.readRawFileDescriptor();
+            }
+            return new ParcelFileDescriptor(fd, commChannel);
+        }
+
+        @Override
+        public ParcelFileDescriptor[] newArray(int size) {
+            return new ParcelFileDescriptor[size];
+        }
+    };
+
+    /**
+     * Callback indicating that a ParcelFileDescriptor has been closed.
+     */
+    public interface OnCloseListener {
+        /**
+         * Event indicating the ParcelFileDescriptor to which this listener was
+         * attached has been closed.
+         *
+         * @param e error state, or {@code null} if closed cleanly.
+         *        If the close event was the result of
+         *        {@link ParcelFileDescriptor#detachFd()}, this will be a
+         *        {@link FileDescriptorDetachedException}. After detach the
+         *        remote side may continue reading/writing to the underlying
+         *        {@link FileDescriptor}, but they can no longer deliver
+         *        reliable close/error events.
+         */
+        public void onClose(IOException e);
+    }
+
+    /**
+     * Exception that indicates that the file descriptor was detached.
+     */
+    public static class FileDescriptorDetachedException extends IOException {
+
+        private static final long serialVersionUID = 0xDe7ac4edFdL;
+
+        public FileDescriptorDetachedException() {
+            super("Remote side is detached");
+        }
+    }
+
+    /**
+     * Internal class representing a remote status read by
+     * {@link ParcelFileDescriptor#readCommStatus(FileDescriptor, byte[])}.
+     *
+     * Warning: this must be kept in sync with ParcelFileDescriptorStatus at
+     * frameworks/native/libs/binder/Parcel.cpp
+     */
+    private static class Status {
+        /** Special value indicating remote side died. */
+        public static final int DEAD = -2;
+        /** Special value indicating no status should be written. */
+        public static final int SILENCE = -1;
+
+        /** Remote reported that everything went better than expected. */
+        public static final int OK = 0;
+        /** Remote reported error; length and message follow. */
+        public static final int ERROR = 1;
+        /** Remote reported {@link #detachFd()} and went rogue. */
+        public static final int DETACHED = 2;
+        /** Remote reported their object was finalized. */
+        public static final int LEAKED = 3;
+
+        public final int status;
+        public final String msg;
+
+        public Status(int status) {
+            this(status, null);
+        }
+
+        public Status(int status, String msg) {
+            this.status = status;
+            this.msg = msg;
+        }
+
+        public IOException asIOException() {
+            switch (status) {
+                case DEAD:
+                    return new IOException("Remote side is dead");
+                case OK:
+                    return null;
+                case ERROR:
+                    return new IOException("Remote error: " + msg);
+                case DETACHED:
+                    return new FileDescriptorDetachedException();
+                case LEAKED:
+                    return new IOException("Remote side was leaked");
+                default:
+                    return new IOException("Unknown status: " + status);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "{" + status + ": " + msg + "}";
+        }
+    }
+
+    private static boolean isAtLeastQ() {
+        return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
+    }
+
+    private static int ifAtLeastQ(int value) {
+        return isAtLeastQ() ? value : 0;
+    }
+}
diff --git a/android-34/android/os/ParcelFormatException.java b/android-34/android/os/ParcelFormatException.java
new file mode 100644
index 0000000..8b6fda0
--- /dev/null
+++ b/android-34/android/os/ParcelFormatException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+/**
+ * The contents of a Parcel (usually during unmarshalling) does not
+ * contain the expected data.
+ */
+public class ParcelFormatException extends RuntimeException {
+    public ParcelFormatException() {
+        super();
+    }
+
+    public ParcelFormatException(String reason) {
+        super(reason);
+    }
+}
diff --git a/android-34/android/os/ParcelObtainPerfTest.java b/android-34/android/os/ParcelObtainPerfTest.java
new file mode 100644
index 0000000..760ae12
--- /dev/null
+++ b/android-34/android/os/ParcelObtainPerfTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ParcelObtainPerfTest {
+    private static final int ITERATIONS = 1_000_000;
+
+    @Test
+    public void timeContention_01() throws Exception {
+        timeContention(1);
+    }
+
+    @Test
+    public void timeContention_04() throws Exception {
+        timeContention(4);
+    }
+
+    @Test
+    public void timeContention_16() throws Exception {
+        timeContention(16);
+    }
+
+    private static void timeContention(int numThreads) throws Exception {
+        final long start = SystemClock.elapsedRealtime();
+        {
+            final ObtainThread[] threads = new ObtainThread[numThreads];
+            for (int i = 0; i < numThreads; i++) {
+                final ObtainThread thread = new ObtainThread(ITERATIONS / numThreads);
+                thread.start();
+                threads[i] = thread;
+            }
+            for (int i = 0; i < numThreads; i++) {
+                threads[i].join();
+            }
+        }
+        final long duration = SystemClock.elapsedRealtime() - start;
+
+        final Bundle results = new Bundle();
+        results.putLong("duration", duration);
+        InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
+    }
+
+    public static class ObtainThread extends Thread {
+        public int iterations;
+
+        public ObtainThread(int iterations) {
+            this.iterations = iterations;
+        }
+
+        @Override
+        public void run() {
+            while (iterations-- > 0) {
+                final Parcel data = Parcel.obtain();
+                final Parcel reply = Parcel.obtain();
+                try {
+                    data.writeInt(32);
+                    reply.writeInt(32);
+                } finally {
+                    reply.recycle();
+                    data.recycle();
+                }
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/ParcelPerfTest.java b/android-34/android/os/ParcelPerfTest.java
new file mode 100644
index 0000000..be2f9d7
--- /dev/null
+++ b/android-34/android/os/ParcelPerfTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ParcelPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private Parcel mParcel;
+
+    @Before
+    public void setUp() {
+        mParcel = Parcel.obtain();
+        mParcel.setDataPosition(0);
+        mParcel.setDataCapacity(8);
+    }
+
+    @After
+    public void tearDown() {
+        mParcel.recycle();
+        mParcel = null;
+    }
+
+    @Test
+    public void timeSetDataPosition() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.setDataPosition(0);
+        }
+    }
+
+    @Test
+    public void timeGetDataPosition() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.dataPosition();
+        }
+    }
+
+    @Test
+    public void timeSetDataSize() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.setDataSize(0);
+        }
+    }
+
+    @Test
+    public void timeGetDataSize() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.dataSize();
+        }
+    }
+
+    @Test
+    public void timeSetDataCapacity() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.setDataCapacity(0);
+        }
+    }
+
+    @Test
+    public void timeGetDataCapacity() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.dataCapacity();
+        }
+    }
+
+    @Test
+    public void timeWriteByte() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final byte val = 0xF;
+        while (state.keepRunning()) {
+            mParcel.setDataPosition(0);
+            mParcel.writeByte(val);
+        }
+    }
+
+    @Test
+    public void timeReadByte() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.setDataPosition(0);
+            mParcel.readByte();
+        }
+    }
+
+    @Test
+    public void timeWriteInt() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final int val = 0xF;
+        while (state.keepRunning()) {
+            mParcel.setDataPosition(0);
+            mParcel.writeInt(val);
+        }
+    }
+
+    @Test
+    public void timeReadInt() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.setDataPosition(0);
+            mParcel.readInt();
+        }
+    }
+
+    @Test
+    public void timeWriteLong() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final long val = 0xF;
+        while (state.keepRunning()) {
+            mParcel.setDataPosition(0);
+            mParcel.writeLong(val);
+        }
+    }
+
+    @Test
+    public void timeReadLong() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mParcel.setDataPosition(0);
+            mParcel.readLong();
+        }
+    }
+
+    @Test
+    public void timeWriteException() {
+        timeWriteException(false);
+    }
+
+    @Test
+    public void timeWriteExceptionWithStackTraceParceling() {
+        timeWriteException(true);
+    }
+
+    @Test
+    public void timeReadException() {
+        timeReadException(false);
+    }
+
+    @Test
+    public void timeReadExceptionWithStackTraceParceling() {
+        timeReadException(true);
+    }
+
+    private void timeWriteException(boolean enableParceling) {
+        if (enableParceling) {
+            Parcel.setStackTraceParceling(true);
+        }
+        try {
+            final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            Parcel p = Parcel.obtain();
+            SecurityException e = new SecurityException("TestMessage");
+            while (state.keepRunning()) {
+                p.setDataPosition(0);
+                p.writeException(e);
+            }
+        } finally {
+            if (enableParceling) {
+                Parcel.setStackTraceParceling(false);
+            }
+        }
+    }
+
+    private void timeReadException(boolean enableParceling) {
+        if (enableParceling) {
+            Parcel.setStackTraceParceling(true);
+        }
+        try {
+            final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            Parcel p = Parcel.obtain();
+            String msg = "TestMessage";
+            p.writeException(new SecurityException(msg));
+            p.setDataPosition(0);
+            // First verify that remote cause is set (if parceling is enabled)
+            try {
+                p.readException();
+            } catch (SecurityException e) {
+                assertEquals(e.getMessage(), msg);
+                if (enableParceling) {
+                    assertTrue(e.getCause() instanceof RemoteException);
+                } else {
+                    assertNull(e.getCause());
+                }
+            }
+
+            while (state.keepRunning()) {
+                p.setDataPosition(0);
+                try {
+                    p.readException();
+                } catch (SecurityException expected) {
+                }
+            }
+        } finally {
+            if (enableParceling) {
+                Parcel.setStackTraceParceling(false);
+            }
+        }
+    }
+
+}
diff --git a/android-34/android/os/ParcelStringPerfTest.java b/android-34/android/os/ParcelStringPerfTest.java
new file mode 100644
index 0000000..2b861cb
--- /dev/null
+++ b/android-34/android/os/ParcelStringPerfTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@LargeTest
+@RunWith(Parameterized.class)
+public class ParcelStringPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameterized.Parameter(0)
+    public String mName;
+    @Parameterized.Parameter(1)
+    public String mValue;
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> getParameters() {
+        return Arrays.asList(new Object[][] {
+                { "simple", "com.example.typical_package_name" },
+                { "complex", "從不喜歡孤單一個 - 蘇永康/吳雨霏" },
+        });
+    }
+
+    @Test
+    public void timeWriteString8() {
+        final Parcel parcel = Parcel.obtain();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            parcel.setDataPosition(0);
+            parcel.writeString8(mValue);
+        }
+    }
+
+    @Test
+    public void timeWriteString16() {
+        final Parcel parcel = Parcel.obtain();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            parcel.setDataPosition(0);
+            parcel.writeString16(mValue);
+        }
+    }
+}
diff --git a/android-34/android/os/ParcelUuid.java b/android-34/android/os/ParcelUuid.java
new file mode 100644
index 0000000..b529694
--- /dev/null
+++ b/android-34/android/os/ParcelUuid.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ * 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.os;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import java.util.UUID;
+
+/**
+ * This class is a Parcelable wrapper around {@link UUID} which is an
+ * immutable representation of a 128-bit universally unique
+ * identifier.
+ */
+public final class ParcelUuid implements Parcelable {
+
+    private final UUID mUuid;
+
+    /**
+     * Constructor creates a ParcelUuid instance from the
+     * given {@link UUID}.
+     *
+     * @param uuid UUID
+     */
+    public ParcelUuid(UUID uuid) {
+        mUuid = uuid;
+    }
+
+    /**
+     * Creates a new ParcelUuid from a string representation of {@link UUID}.
+     *
+     * @param uuid
+     *            the UUID string to parse.
+     * @return a ParcelUuid instance.
+     * @throws NullPointerException
+     *             if {@code uuid} is {@code null}.
+     * @throws IllegalArgumentException
+     *             if {@code uuid} is not formatted correctly.
+     */
+    public static ParcelUuid fromString(String uuid) {
+        return new ParcelUuid(UUID.fromString(uuid));
+    }
+
+    /**
+     * Get the {@link UUID} represented by the ParcelUuid.
+     *
+     * @return UUID contained in the ParcelUuid.
+     */
+    public UUID getUuid() {
+        return mUuid;
+    }
+
+    /**
+     * Returns a string representation of the ParcelUuid
+     * For example: 0000110B-0000-1000-8000-00805F9B34FB will be the return value.
+     *
+     * @return a String instance.
+     */
+    @Override
+    public String toString() {
+        return mUuid.toString();
+    }
+
+
+   @Override
+   public int hashCode() {
+       return mUuid.hashCode();
+   }
+
+   /**
+    * Compares this ParcelUuid to another object for equality. If {@code object}
+    * is not {@code null}, is a ParcelUuid instance, and all bits are equal, then
+    * {@code true} is returned.
+    *
+    * @param object
+    *            the {@code Object} to compare to.
+    * @return {@code true} if this ParcelUuid is equal to {@code object}
+    *         or {@code false} if not.
+    */
+   @Override
+   public boolean equals(@Nullable Object object) {
+       if (object == null) {
+           return false;
+       }
+
+       if (this == object) {
+           return true;
+       }
+
+       if (!(object instanceof ParcelUuid)) {
+           return false;
+       }
+
+       ParcelUuid that = (ParcelUuid) object;
+
+       return (this.mUuid.equals(that.mUuid));
+   }
+
+   public static final @android.annotation.NonNull Parcelable.Creator<ParcelUuid> CREATOR =
+               new Parcelable.Creator<ParcelUuid>() {
+        @UnsupportedAppUsage
+        public ParcelUuid createFromParcel(Parcel source) {
+            long mostSigBits = source.readLong();
+            long leastSigBits = source.readLong();
+            UUID uuid = new UUID(mostSigBits, leastSigBits);
+            return new ParcelUuid(uuid);
+        }
+
+        public ParcelUuid[] newArray(int size) {
+            return new ParcelUuid[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mUuid.getMostSignificantBits());
+        dest.writeLong(mUuid.getLeastSignificantBits());
+    }
+}
diff --git a/android-34/android/os/Parcelable.java b/android-34/android/os/Parcelable.java
new file mode 100644
index 0000000..f2b60a4
--- /dev/null
+++ b/android-34/android/os/Parcelable.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for classes whose instances can be written to
+ * and restored from a {@link Parcel}.  Classes implementing the Parcelable
+ * interface must also have a non-null public static field called
+ * <code>CREATOR</code> of a type that implements the {@link Parcelable.Creator}
+ * interface.
+ *
+ * <p>A typical implementation of Parcelable is:</p>
+ *
+ * <div>
+ * <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
+ * <pre class="prettyprint lang-kotlin">
+ * class MyParcelable private constructor(`in`: Parcel) : Parcelable {
+ *     private val mData: Int = `in`.readInt()
+ *
+ *     override fun describeContents(): Int {
+ *         return 0
+ *     }
+ *
+ *     override fun writeToParcel(out: Parcel, flags: Int) {
+ *         out.writeInt(mData)
+ *     }
+ *
+ *     companion object CREATOR: Parcelable.Creator&lt;MyParcelable?&gt; {
+ *         override fun createFromParcel(`in`: Parcel): MyParcelable? {
+ *             return MyParcelable(`in`)
+ *         }
+ *
+ *         override fun newArray(size: Int): Array&lt;MyParcelable?&gt; {
+ *             return arrayOfNulls(size)
+ *         }
+ *     }
+ * }
+ * </pre>
+ * </section><section><h3 id="java">Java</h3>
+ * <pre class="prettyprint lang-java">
+ * public class MyParcelable implements Parcelable {
+ *     private int mData;
+ *
+ *     public int describeContents() {
+ *         return 0;
+ *     }
+ *
+ *     public void writeToParcel(Parcel out, int flags) {
+ *         out.writeInt(mData);
+ *     }
+ *
+ *     public static final Parcelable.Creator&lt;MyParcelable&gt; CREATOR
+ *             = new Parcelable.Creator&lt;MyParcelable&gt;() {
+ *         public MyParcelable createFromParcel(Parcel in) {
+ *             return new MyParcelable(in);
+ *         }
+ *
+ *         public MyParcelable[] newArray(int size) {
+ *             return new MyParcelable[size];
+ *         }
+ *     };
+ *
+ *     private MyParcelable(Parcel in) {
+ *         mData = in.readInt();
+ *     }
+ * }</pre></section></div></div>
+ */
+public interface Parcelable {
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PARCELABLE_" }, value = {
+            PARCELABLE_WRITE_RETURN_VALUE,
+            PARCELABLE_ELIDE_DUPLICATES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WriteFlags {}
+
+    /**
+     * Flag for use with {@link #writeToParcel}: the object being written
+     * is a return value, that is the result of a function such as
+     * "<code>Parcelable someFunction()</code>",
+     * "<code>void someFunction(out Parcelable)</code>", or
+     * "<code>void someFunction(inout Parcelable)</code>".  Some implementations
+     * may want to release resources at this point.
+     */
+    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
+
+    /**
+     * Flag for use with {@link #writeToParcel}: a parent object will take
+     * care of managing duplicate state/data that is nominally replicated
+     * across its inner data members.  This flag instructs the inner data
+     * types to omit that data during marshaling.  Exact behavior may vary
+     * on a case by case basis.
+     * @hide
+     */
+    public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
+
+    /*
+     * Bit masks for use with {@link #describeContents}: each bit represents a
+     * kind of object considered to have potential special significance when
+     * marshalled.
+     */
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "CONTENTS_" }, value = {
+            CONTENTS_FILE_DESCRIPTOR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ContentsFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PARCELABLE_STABILITY_" }, value = {
+            PARCELABLE_STABILITY_LOCAL,
+            PARCELABLE_STABILITY_VINTF,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Stability {}
+
+    /**
+     * Something that is not meant to cross compilation boundaries.
+     *
+     * Note: unlike binder/Stability.h which uses bitsets to detect stability,
+     * since we don't currently have a notion of different local locations,
+     * higher stability levels are formed at higher levels.
+     *
+     * For instance, contained entirely within system partitions.
+     * @see #getStability()
+     * @see ParcelableHolder
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    public static final int PARCELABLE_STABILITY_LOCAL = 0x0000;
+    /**
+     * Something that is meant to be used between system and vendor.
+     * @see #getStability()
+     * @see ParcelableHolder
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    public static final int PARCELABLE_STABILITY_VINTF = 0x0001;
+
+    /**
+     * Descriptor bit used with {@link #describeContents()}: indicates that
+     * the Parcelable object's flattened representation includes a file descriptor.
+     *
+     * @see #describeContents()
+     */
+    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
+    
+    /**
+     * Describe the kinds of special objects contained in this Parcelable
+     * instance's marshaled representation. For example, if the object will
+     * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
+     * the return value of this method must include the
+     * {@link #CONTENTS_FILE_DESCRIPTOR} bit.
+     *  
+     * @return a bitmask indicating the set of special object types marshaled
+     * by this Parcelable object instance.
+     */
+    public @ContentsFlags int describeContents();
+
+    /**
+     * 'Stable' means this parcelable is guaranteed to be stable for multiple years.
+     * It must be guaranteed by setting stability field in aidl_interface,
+     * OR explicitly override this method from @JavaOnlyStableParcelable marked Parcelable.
+     * WARNING: isStable() is only expected to be overridden by auto-generated code,
+     * OR @JavaOnlyStableParcelable marked Parcelable only if there is guaranteed to
+     * be only once copy of the parcelable on the system.
+     * @return true if this parcelable is stable.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    default @Stability int getStability() {
+        return PARCELABLE_STABILITY_LOCAL;
+    }
+
+    /**
+     * Flatten this object in to a Parcel.
+     * 
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+     */
+    public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags);
+
+    /**
+     * Interface that must be implemented and provided as a public CREATOR
+     * field that generates instances of your Parcelable class from a Parcel.
+     */
+    public interface Creator<T> {
+        /**
+         * Create a new instance of the Parcelable class, instantiating it
+         * from the given Parcel whose data had previously been written by
+         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
+         * 
+         * @param source The Parcel to read the object's data from.
+         * @return Returns a new instance of the Parcelable class.
+         */
+        public T createFromParcel(Parcel source);
+        
+        /**
+         * Create a new array of the Parcelable class.
+         * 
+         * @param size Size of the array.
+         * @return Returns an array of the Parcelable class, with every entry
+         * initialized to null.
+         */
+        public T[] newArray(int size);
+    }
+
+    /**
+     * Specialization of {@link Creator} that allows you to receive the
+     * ClassLoader the object is being created in.
+     */
+    public interface ClassLoaderCreator<T> extends Creator<T> {
+        /**
+         * Create a new instance of the Parcelable class, instantiating it
+         * from the given Parcel whose data had previously been written by
+         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
+         * using the given ClassLoader.
+         *
+         * @param source The Parcel to read the object's data from.
+         * @param loader The ClassLoader that this object is being created in.
+         * @return Returns a new instance of the Parcelable class.
+         */
+        public T createFromParcel(Parcel source, ClassLoader loader);
+    }
+}
diff --git a/android-34/android/os/ParcelableException.java b/android-34/android/os/ParcelableException.java
new file mode 100644
index 0000000..81b9d15
--- /dev/null
+++ b/android-34/android/os/ParcelableException.java
@@ -0,0 +1,90 @@
+/*
+ * 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.os;
+
+import java.io.IOException;
+
+/**
+ * Wrapper class that offers to transport typical {@link Throwable} across a
+ * {@link Binder} call. This class is typically used to transport exceptions
+ * that cannot be modified to add {@link Parcelable} behavior, such as
+ * {@link IOException}.
+ * <ul>
+ * <li>The wrapped throwable must be defined as system class (that is, it must
+ * be in the same {@link ClassLoader} as {@link Parcelable}).
+ * <li>The wrapped throwable must support the
+ * {@link Throwable#Throwable(String)} constructor.
+ * <li>The receiver side must catch any thrown {@link ParcelableException} and
+ * call {@link #maybeRethrow(Class)} for all expected exception types.
+ * </ul>
+ *
+ * @hide
+ */
+public final class ParcelableException extends RuntimeException implements Parcelable {
+    public ParcelableException(Throwable t) {
+        super(t);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends Throwable> void maybeRethrow(Class<T> clazz) throws T {
+        if (clazz.isAssignableFrom(getCause().getClass())) {
+            throw (T) getCause();
+        }
+    }
+
+    /** {@hide} */
+    public static Throwable readFromParcel(Parcel in) {
+        final String name = in.readString();
+        final String msg = in.readString();
+        try {
+            final Class<?> clazz = Class.forName(name, true, Parcelable.class.getClassLoader());
+            if (Throwable.class.isAssignableFrom(clazz)) {
+                return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+            }
+        } catch (ReflectiveOperationException e) {
+        }
+        return new RuntimeException(name + ": " + msg);
+    }
+
+    /** {@hide} */
+    public static void writeToParcel(Parcel out, Throwable t) {
+        out.writeString(t.getClass().getName());
+        out.writeString(t.getMessage());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeToParcel(dest, getCause());
+    }
+
+    public static final @android.annotation.NonNull Creator<ParcelableException> CREATOR = new Creator<ParcelableException>() {
+        @Override
+        public ParcelableException createFromParcel(Parcel source) {
+            return new ParcelableException(readFromParcel(source));
+        }
+
+        @Override
+        public ParcelableException[] newArray(int size) {
+            return new ParcelableException[size];
+        }
+    };
+}
diff --git a/android-34/android/os/ParcelableHolder.java b/android-34/android/os/ParcelableHolder.java
new file mode 100644
index 0000000..a739ba3
--- /dev/null
+++ b/android-34/android/os/ParcelableHolder.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.util.MathUtils;
+
+/**
+ * ParcelableHolder is a Parcelable which can contain another Parcelable.
+ * The main use case of ParcelableHolder is to make a Parcelable extensible.
+ * For example, an AOSP-defined Parcelable <code>AospDefinedParcelable</code>
+ * is expected to be extended by device implementers for their value-add features.
+ * Previously without ParcelableHolder, the device implementers had to
+ * directly modify the Parcelable to add more fields:
+ * <pre> {@code
+ * parcelable AospDefinedParcelable {
+ *   int a;
+ *   String b;
+ *   String x; // added by a device implementer
+ *   int[] y; // added by a device implementer
+ * }}</pre>
+ *
+ * This practice is very error-prone because the fields added by the device implementer
+ * might have a conflict when the Parcelable is revisioned in the next releases of Android.
+ *
+ * Using ParcelableHolder, one can define an extension point in a Parcelable.
+ * <pre> {@code
+ * parcelable AospDefinedParcelable {
+ *   int a;
+ *   String b;
+ *   ParcelableHolder extension;
+ * }}</pre>
+ * Then the device implementers can define their own Parcelable for their extension.
+ *
+ * <pre> {@code
+ * parcelable OemDefinedParcelable {
+ *   String x;
+ *   int[] y;
+ * }}</pre>
+ * Finally, the new Parcelable can be attached to the original Parcelable via
+ * the ParcelableHolder field.
+ *
+ * <pre> {@code
+ * AospDefinedParcelable ap = ...;
+ * OemDefinedParcelable op = new OemDefinedParcelable();
+ * op.x = ...;
+ * op.y = ...;
+ * ap.extension.setParcelable(op);}</pre>
+ *
+ * <p class="note">ParcelableHolder is <strong>not</strong> thread-safe.</p>
+ *
+ * @hide
+ */
+@SystemApi
+public final class ParcelableHolder implements Parcelable {
+    /**
+     * This is set by {@link #setParcelable}.
+     * {@link #mParcelable} and {@link #mParcel} are mutually exclusive
+     * if {@link ParcelableHolder} contains value, otherwise, both are null.
+     */
+    private Parcelable mParcelable;
+    /**
+     * This is set by {@link #readFromParcel}.
+     * {@link #mParcelable} and {@link #mParcel} are mutually exclusive
+     * if {@link ParcelableHolder} contains value, otherwise, both are null.
+     */
+    private Parcel mParcel;
+    private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL;
+
+    public ParcelableHolder(@Parcelable.Stability int stability) {
+        mStability = stability;
+    }
+
+    private ParcelableHolder() {
+
+    }
+
+    /**
+     * {@link ParcelableHolder}'s stability is determined by the parcelable
+     * which contains this ParcelableHolder.
+     * For more detail refer to {@link Parcelable#getStability}.
+     */
+    @Override
+    public @Parcelable.Stability int getStability() {
+        return mStability;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParcelableHolder> CREATOR =
+            new Parcelable.Creator<ParcelableHolder>() {
+                @NonNull
+                @Override
+                public ParcelableHolder createFromParcel(@NonNull Parcel parcel) {
+                    ParcelableHolder parcelable = new ParcelableHolder();
+                    parcelable.readFromParcel(parcel);
+                    return parcelable;
+                }
+
+                @NonNull
+                @Override
+                public ParcelableHolder[] newArray(int size) {
+                    return new ParcelableHolder[size];
+                }
+            };
+
+
+    /**
+     * Write a parcelable into ParcelableHolder, the previous parcelable will be removed.
+     * (@link #setParcelable} and (@link #getParcelable} are not thread-safe.
+     * @throws BadParcelableException if the parcelable's stability is more unstable
+     *         ParcelableHolder.
+     */
+    public void setParcelable(@Nullable Parcelable p) {
+        // A ParcelableHolder can only hold things at its stability or higher.
+        if (p != null && this.getStability() > p.getStability()) {
+            throw new BadParcelableException(
+                "A ParcelableHolder can only hold things at its stability or higher. "
+                + "The ParcelableHolder's stability is " + this.getStability()
+                + ", but the parcelable's stability is " + p.getStability());
+        }
+        mParcelable = p;
+        if (mParcel != null) {
+            mParcel.recycle();
+            mParcel = null;
+        }
+    }
+
+    /**
+     * Read a parcelable from ParcelableHolder.
+     * (@link #setParcelable} and (@link #getParcelable} are not thread-safe.
+     * @return the parcelable that was written by {@link #setParcelable} or {@link #readFromParcel},
+     *         or {@code null} if the parcelable has not been written.
+     * @throws BadParcelableException if T is different from the type written by
+     *         (@link #setParcelable}.
+     */
+    @Nullable
+    public <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) {
+        if (mParcel == null) {
+            if (mParcelable != null && !clazz.isInstance(mParcelable)) {
+                throw new BadParcelableException(
+                    "The ParcelableHolder has " + mParcelable.getClass().getName()
+                    + ", but the requested type is " + clazz.getName());
+            }
+            return (T) mParcelable;
+        }
+
+        mParcel.setDataPosition(0);
+
+        T parcelable = mParcel.readParcelable(clazz.getClassLoader());
+        if (parcelable != null && !clazz.isInstance(parcelable)) {
+            throw new BadParcelableException(
+                    "The ParcelableHolder has " + parcelable.getClass().getName()
+                    + ", but the requested type is " + clazz.getName());
+        }
+        mParcelable = parcelable;
+
+        mParcel.recycle();
+        mParcel = null;
+        return parcelable;
+    }
+
+    /**
+     * Read ParcelableHolder from a parcel.
+     */
+    public void readFromParcel(@NonNull Parcel parcel) {
+        int wireStability = parcel.readInt();
+        if (this.mStability != wireStability) {
+            throw new IllegalArgumentException("Expected stability " + this.mStability
+                                               + " but got " + wireStability);
+        }
+
+        mParcelable = null;
+
+        int dataSize = parcel.readInt();
+        if (dataSize < 0) {
+            throw new IllegalArgumentException("dataSize from parcel is negative");
+        } else if (dataSize == 0) {
+            if (mParcel != null) {
+                mParcel.recycle();
+                mParcel = null;
+            }
+            return;
+        }
+        if (mParcel == null) {
+            mParcel = Parcel.obtain();
+        }
+        mParcel.setDataPosition(0);
+        mParcel.setDataSize(0);
+        int dataStartPos = parcel.dataPosition();
+
+        mParcel.appendFrom(parcel, dataStartPos, dataSize);
+        parcel.setDataPosition(MathUtils.addOrThrow(dataStartPos, dataSize));
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeInt(this.mStability);
+
+        if (mParcel != null) {
+            parcel.writeInt(mParcel.dataSize());
+            parcel.appendFrom(mParcel, 0, mParcel.dataSize());
+            return;
+        }
+
+        if (mParcelable == null) {
+            parcel.writeInt(0);
+            return;
+        }
+
+        int sizePos = parcel.dataPosition();
+        parcel.writeInt(0);
+        int dataStartPos = parcel.dataPosition();
+        parcel.writeParcelable(mParcelable, 0);
+        int dataSize = parcel.dataPosition() - dataStartPos;
+
+        parcel.setDataPosition(sizePos);
+        parcel.writeInt(dataSize);
+        parcel.setDataPosition(MathUtils.addOrThrow(parcel.dataPosition(), dataSize));
+    }
+
+    @Override
+    public int describeContents() {
+        if (mParcel != null) {
+            return mParcel.hasFileDescriptors() ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+        }
+        if (mParcelable != null) {
+            return mParcelable.describeContents();
+        }
+        return 0;
+    }
+}
diff --git a/android-34/android/os/ParcelableParcel.java b/android-34/android/os/ParcelableParcel.java
new file mode 100644
index 0000000..3be630f
--- /dev/null
+++ b/android-34/android/os/ParcelableParcel.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.MathUtils;
+
+/**
+ * Parcelable containing a raw Parcel of data.
+ * @hide
+ */
+public class ParcelableParcel implements Parcelable {
+    final Parcel mParcel;
+    final ClassLoader mClassLoader;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ParcelableParcel(ClassLoader loader) {
+        mParcel = Parcel.obtain();
+        mClassLoader = loader;
+    }
+
+    public ParcelableParcel(Parcel src, ClassLoader loader) {
+        mParcel = Parcel.obtain();
+        mClassLoader = loader;
+        int size = src.readInt();
+        if (size < 0) {
+            throw new IllegalArgumentException("Negative size read from parcel");
+        }
+
+        int pos = src.dataPosition();
+        src.setDataPosition(MathUtils.addOrThrow(pos, size));
+        mParcel.appendFrom(src, pos, size);
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public Parcel getParcel() {
+        mParcel.setDataPosition(0);
+        return mParcel;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ClassLoader getClassLoader() {
+        return mClassLoader;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mParcel.dataSize());
+        dest.appendFrom(mParcel, 0, mParcel.dataSize());
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final Parcelable.ClassLoaderCreator<ParcelableParcel> CREATOR
+            = new Parcelable.ClassLoaderCreator<ParcelableParcel>() {
+        public ParcelableParcel createFromParcel(Parcel in) {
+            return new ParcelableParcel(in, null);
+        }
+
+        public ParcelableParcel createFromParcel(Parcel in, ClassLoader loader) {
+            return new ParcelableParcel(in, loader);
+        }
+
+        public ParcelableParcel[] newArray(int size) {
+            return new ParcelableParcel[size];
+        }
+    };
+}
diff --git a/android-34/android/os/PatternMatcher.java b/android-34/android/os/PatternMatcher.java
new file mode 100644
index 0000000..b5425b4
--- /dev/null
+++ b/android-34/android/os/PatternMatcher.java
@@ -0,0 +1,600 @@
+/*
+ * 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.
+ * 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.os;
+
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import java.util.Arrays;
+
+/**
+ * A simple pattern matcher, which is safe to use on untrusted data: it does
+ * not provide full reg-exp support, only simple globbing that can not be
+ * used maliciously.
+ */
+public class PatternMatcher implements Parcelable {
+    /**
+     * Pattern type: the given pattern must exactly match the string it is
+     * tested against.
+     */
+    public static final int PATTERN_LITERAL = 0;
+    
+    /**
+     * Pattern type: the given pattern must match the
+     * beginning of the string it is tested against.
+     */
+    public static final int PATTERN_PREFIX = 1;
+    
+    /**
+     * Pattern type: the given pattern is interpreted with a
+     * simple glob syntax for matching against the string it is tested against.
+     * In this syntax, you can use the '*' character to match against zero or
+     * more occurrences of the character immediately before.  If the
+     * character before it is '.' it will match any character.  The character
+     * '\' can be used as an escape.  This essentially provides only the '*'
+     * wildcard part of a normal regexp. 
+     */
+    public static final int PATTERN_SIMPLE_GLOB = 2;
+
+    /**
+     * Pattern type: the given pattern is interpreted with a regular
+     * expression-like syntax for matching against the string it is tested
+     * against. Supported tokens include dot ({@code .}) and sets ({@code [...]})
+     * with full support for character ranges and the not ({@code ^}) modifier.
+     * Supported modifiers include star ({@code *}) for zero-or-more, plus ({@code +})
+     * for one-or-more and full range ({@code {...}}) support. This is a simple
+     * evaluation implementation in which matching is done against the pattern in
+     * real time with no backtracking support.
+     */
+    public static final int PATTERN_ADVANCED_GLOB = 3;
+
+    /**
+     * Pattern type: the given pattern must match the
+     * end of the string it is tested against.
+     */
+    public static final int PATTERN_SUFFIX = 4;
+
+    // token types for advanced matching
+    private static final int TOKEN_TYPE_LITERAL = 0;
+    private static final int TOKEN_TYPE_ANY = 1;
+    private static final int TOKEN_TYPE_SET = 2;
+    private static final int TOKEN_TYPE_INVERSE_SET = 3;
+
+    // Return for no match
+    private static final int NO_MATCH = -1;
+
+    private static final String TAG = "PatternMatcher";
+
+    // Parsed placeholders for advanced patterns
+    private static final int PARSED_TOKEN_CHAR_SET_START = -1;
+    private static final int PARSED_TOKEN_CHAR_SET_INVERSE_START = -2;
+    private static final int PARSED_TOKEN_CHAR_SET_STOP = -3;
+    private static final int PARSED_TOKEN_CHAR_ANY = -4;
+    private static final int PARSED_MODIFIER_RANGE_START = -5;
+    private static final int PARSED_MODIFIER_RANGE_STOP = -6;
+    private static final int PARSED_MODIFIER_ZERO_OR_MORE = -7;
+    private static final int PARSED_MODIFIER_ONE_OR_MORE = -8;
+
+    private final String mPattern;
+    private final int mType;
+    private final int[] mParsedPattern;
+
+
+    private static final int MAX_PATTERN_STORAGE = 2048;
+    // workspace to use for building a parsed advanced pattern;
+    private static final int[] sParsedPatternScratch = new int[MAX_PATTERN_STORAGE];
+
+    public PatternMatcher(String pattern, int type) {
+        mPattern = pattern;
+        mType = type;
+        if (mType == PATTERN_ADVANCED_GLOB) {
+            mParsedPattern = parseAndVerifyAdvancedPattern(pattern);
+        } else {
+            mParsedPattern = null;
+        }
+    }
+
+    public final String getPath() {
+        return mPattern;
+    }
+    
+    public final int getType() {
+        return mType;
+    }
+    
+    public boolean match(String str) {
+        return matchPattern(str, mPattern, mParsedPattern, mType);
+    }
+
+    public String toString() {
+        String type = "? ";
+        switch (mType) {
+            case PATTERN_LITERAL:
+                type = "LITERAL: ";
+                break;
+            case PATTERN_PREFIX:
+                type = "PREFIX: ";
+                break;
+            case PATTERN_SIMPLE_GLOB:
+                type = "GLOB: ";
+                break;
+            case PATTERN_ADVANCED_GLOB:
+                type = "ADVANCED: ";
+                break;
+            case PATTERN_SUFFIX:
+                type = "SUFFIX: ";
+                break;
+        }
+        return "PatternMatcher{" + type + mPattern + "}";
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(PatternMatcherProto.PATTERN, mPattern);
+        proto.write(PatternMatcherProto.TYPE, mType);
+        // PatternMatcherProto.PARSED_PATTERN is too much to dump, but the field is reserved to
+        // match the current data structure.
+        proto.end(token);
+    }
+
+    /**
+     * Perform a check on the matcher for the pattern type of {@link #PATTERN_ADVANCED_GLOB}.
+     * Return true if it passed.
+     * @hide
+     */
+    public boolean check() {
+        try {
+            if (mType == PATTERN_ADVANCED_GLOB) {
+                return Arrays.equals(mParsedPattern, parseAndVerifyAdvancedPattern(mPattern));
+            }
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "Failed to verify advanced pattern: " + e.getMessage());
+            return false;
+        }
+        return true;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mPattern);
+        dest.writeInt(mType);
+        dest.writeIntArray(mParsedPattern);
+    }
+
+    public PatternMatcher(Parcel src) {
+        mPattern = src.readString();
+        mType = src.readInt();
+        mParsedPattern = src.createIntArray();
+    }
+    
+    public static final @android.annotation.NonNull Parcelable.Creator<PatternMatcher> CREATOR
+            = new Parcelable.Creator<PatternMatcher>() {
+        public PatternMatcher createFromParcel(Parcel source) {
+            return new PatternMatcher(source);
+        }
+
+        public PatternMatcher[] newArray(int size) {
+            return new PatternMatcher[size];
+        }
+    };
+    
+    static boolean matchPattern(String match, String pattern, int[] parsedPattern, int type) {
+        if (match == null) return false;
+        if (type == PATTERN_LITERAL) {
+            return pattern.equals(match);
+        } if (type == PATTERN_PREFIX) {
+            return match.startsWith(pattern);
+        } else if (type == PATTERN_SIMPLE_GLOB) {
+            return matchGlobPattern(pattern, match);
+        } else if (type == PATTERN_ADVANCED_GLOB) {
+            return matchAdvancedPattern(parsedPattern, match);
+        } else if (type == PATTERN_SUFFIX) {
+            return match.endsWith(pattern);
+        }
+        return false;
+    }
+
+    static boolean matchGlobPattern(String pattern, String match) {
+        final int NP = pattern.length();
+        if (NP <= 0) {
+            return match.length() <= 0;
+        }
+        final int NM = match.length();
+        int ip = 0, im = 0;
+        char nextChar = pattern.charAt(0);
+        while ((ip<NP) && (im<NM)) {
+            char c = nextChar;
+            ip++;
+            nextChar = ip < NP ? pattern.charAt(ip) : 0;
+            final boolean escaped = (c == '\\');
+            if (escaped) {
+                c = nextChar;
+                ip++;
+                nextChar = ip < NP ? pattern.charAt(ip) : 0;
+            }
+            if (nextChar == '*') {
+                if (!escaped && c == '.') {
+                    if (ip >= (NP-1)) {
+                        // at the end with a pattern match, so
+                        // all is good without checking!
+                        return true;
+                    }
+                    ip++;
+                    nextChar = pattern.charAt(ip);
+                    // Consume everything until the next character in the
+                    // pattern is found.
+                    if (nextChar == '\\') {
+                        ip++;
+                        nextChar = ip < NP ? pattern.charAt(ip) : 0;
+                    }
+                    do {
+                        if (match.charAt(im) == nextChar) {
+                            break;
+                        }
+                        im++;
+                    } while (im < NM);
+                    if (im == NM) {
+                        // Whoops, the next character in the pattern didn't
+                        // exist in the match.
+                        return false;
+                    }
+                    ip++;
+                    nextChar = ip < NP ? pattern.charAt(ip) : 0;
+                    im++;
+                } else {
+                    // Consume only characters matching the one before '*'.
+                    do {
+                        if (match.charAt(im) != c) {
+                            break;
+                        }
+                        im++;
+                    } while (im < NM);
+                    ip++;
+                    nextChar = ip < NP ? pattern.charAt(ip) : 0;
+                }
+            } else {
+                if (c != '.' && match.charAt(im) != c) return false;
+                im++;
+            }
+        }
+        
+        if (ip >= NP && im >= NM) {
+            // Reached the end of both strings, all is good!
+            return true;
+        }
+        
+        // One last check: we may have finished the match string, but still
+        // have a '.*' at the end of the pattern, which should still count
+        // as a match.
+        if (ip == NP-2 && pattern.charAt(ip) == '.'
+            && pattern.charAt(ip+1) == '*') {
+            return true;
+        }
+        
+        return false;
+    }
+
+    /**
+     * Parses the advanced pattern and returns an integer array representation of it. The integer
+     * array treats each field as a character if positive and a unique token placeholder if
+     * negative. This method will throw on any pattern structure violations.
+     */
+    synchronized static int[] parseAndVerifyAdvancedPattern(String pattern) {
+        int ip = 0;
+        final int LP = pattern.length();
+
+        int it = 0;
+
+        boolean inSet = false;
+        boolean inRange = false;
+        boolean inCharClass = false;
+
+        boolean addToParsedPattern;
+
+        while (ip < LP) {
+            if (it > MAX_PATTERN_STORAGE - 3) {
+                throw new IllegalArgumentException("Pattern is too large!");
+            }
+
+            char c = pattern.charAt(ip);
+            addToParsedPattern = false;
+
+            switch (c) {
+                case '[':
+                    if (inSet) {
+                        addToParsedPattern = true; // treat as literal or char class in set
+                    } else {
+                        if (pattern.charAt(ip + 1) == '^') {
+                            sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_SET_INVERSE_START;
+                            ip++; // skip over the '^'
+                        } else {
+                            sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_SET_START;
+                        }
+                        ip++; // move to the next pattern char
+                        inSet = true;
+                        continue;
+                    }
+                    break;
+                case ']':
+                    if (!inSet) {
+                        addToParsedPattern = true; // treat as literal outside of set
+                    } else {
+                        int parsedToken = sParsedPatternScratch[it - 1];
+                        if (parsedToken == PARSED_TOKEN_CHAR_SET_START ||
+                            parsedToken == PARSED_TOKEN_CHAR_SET_INVERSE_START) {
+                            throw new IllegalArgumentException(
+                                    "You must define characters in a set.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_SET_STOP;
+                        inSet = false;
+                        inCharClass = false;
+                    }
+                    break;
+                case '{':
+                    if (!inSet) {
+                        if (it == 0 || isParsedModifier(sParsedPatternScratch[it - 1])) {
+                            throw new IllegalArgumentException("Modifier must follow a token.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_RANGE_START;
+                        ip++;
+                        inRange = true;
+                    }
+                    break;
+                case '}':
+                    if (inRange) { // only terminate the range if we're currently in one
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_RANGE_STOP;
+                        inRange = false;
+                    }
+                    break;
+                case '*':
+                    if (!inSet) {
+                        if (it == 0 || isParsedModifier(sParsedPatternScratch[it - 1])) {
+                            throw new IllegalArgumentException("Modifier must follow a token.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_ZERO_OR_MORE;
+                    }
+                    break;
+                case '+':
+                    if (!inSet) {
+                        if (it == 0 || isParsedModifier(sParsedPatternScratch[it - 1])) {
+                            throw new IllegalArgumentException("Modifier must follow a token.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_ONE_OR_MORE;
+                    }
+                    break;
+                case '.':
+                    if (!inSet) {
+                        sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_ANY;
+                    }
+                    break;
+                case '\\': // escape
+                    if (ip + 1 >= LP) {
+                        throw new IllegalArgumentException("Escape found at end of pattern!");
+                    }
+                    c = pattern.charAt(++ip);
+                    addToParsedPattern = true;
+                    break;
+                default:
+                    addToParsedPattern = true;
+                    break;
+            }
+            if (inSet) {
+                if (inCharClass) {
+                    sParsedPatternScratch[it++] = c;
+                    inCharClass = false;
+                } else {
+                    // look forward for character class
+                    if (ip + 2 < LP
+                            && pattern.charAt(ip + 1) == '-'
+                            && pattern.charAt(ip + 2) != ']') {
+                        inCharClass = true;
+                        sParsedPatternScratch[it++] = c; // set first token as lower end of range
+                        ip++; // advance past dash
+                    } else { // literal
+                        sParsedPatternScratch[it++] = c; // set first token as literal
+                        sParsedPatternScratch[it++] = c; // set second set as literal
+                    }
+                }
+            } else if (inRange) {
+                int endOfSet = pattern.indexOf('}', ip);
+                if (endOfSet < 0) {
+                    throw new IllegalArgumentException("Range not ended with '}'");
+                }
+                String rangeString = pattern.substring(ip, endOfSet);
+                int commaIndex = rangeString.indexOf(',');
+                try {
+                    final int rangeMin;
+                    final int rangeMax;
+                    if (commaIndex < 0) {
+                        int parsedRange = Integer.parseInt(rangeString);
+                        rangeMin = rangeMax = parsedRange;
+                    } else {
+                        rangeMin = Integer.parseInt(rangeString.substring(0, commaIndex));
+                        if (commaIndex == rangeString.length() - 1) { // e.g. {n,} (n or more)
+                            rangeMax = Integer.MAX_VALUE;
+                        } else {
+                            rangeMax = Integer.parseInt(rangeString.substring(commaIndex + 1));
+                        }
+                    }
+                    if (rangeMin > rangeMax) {
+                        throw new IllegalArgumentException(
+                            "Range quantifier minimum is greater than maximum");
+                    }
+                    sParsedPatternScratch[it++] = rangeMin;
+                    sParsedPatternScratch[it++] = rangeMax;
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Range number format incorrect", e);
+                }
+                ip = endOfSet;
+                continue; // don't increment ip
+            } else if (addToParsedPattern) {
+                sParsedPatternScratch[it++] = c;
+            }
+            ip++;
+        }
+        if (inSet) {
+            throw new IllegalArgumentException("Set was not terminated!");
+        }
+        return Arrays.copyOf(sParsedPatternScratch, it);
+    }
+
+    private static boolean isParsedModifier(int parsedChar) {
+        return parsedChar == PARSED_MODIFIER_ONE_OR_MORE ||
+                parsedChar == PARSED_MODIFIER_ZERO_OR_MORE ||
+                parsedChar == PARSED_MODIFIER_RANGE_STOP ||
+                parsedChar == PARSED_MODIFIER_RANGE_START;
+    }
+
+    static boolean matchAdvancedPattern(int[] parsedPattern, String match) {
+
+        // create indexes
+        int ip = 0, im = 0;
+
+        // one-time length check
+        final int LP = parsedPattern.length, LM = match.length();
+
+        // The current character being analyzed in the pattern
+        int patternChar;
+
+        int tokenType;
+
+        int charSetStart = 0, charSetEnd = 0;
+
+        while (ip < LP) { // we still have content in the pattern
+
+            patternChar = parsedPattern[ip];
+            // get the match type of the next verb
+
+            switch (patternChar) {
+                case PARSED_TOKEN_CHAR_ANY:
+                    tokenType = TOKEN_TYPE_ANY;
+                    ip++;
+                    break;
+                case PARSED_TOKEN_CHAR_SET_START:
+                case PARSED_TOKEN_CHAR_SET_INVERSE_START:
+                    tokenType = patternChar == PARSED_TOKEN_CHAR_SET_START
+                            ? TOKEN_TYPE_SET
+                            : TOKEN_TYPE_INVERSE_SET;
+                    charSetStart = ip + 1; // start from the char after the set start
+                    while (++ip < LP && parsedPattern[ip] != PARSED_TOKEN_CHAR_SET_STOP);
+                    charSetEnd = ip - 1; // we're on the set stop, end is the previous
+                    ip++; // move the pointer to the next pattern entry
+                    break;
+                default:
+                    charSetStart = ip;
+                    tokenType = TOKEN_TYPE_LITERAL;
+                    ip++;
+                    break;
+            }
+
+            final int minRepetition;
+            final int maxRepetition;
+
+            // look for a match length modifier
+            if (ip >= LP) {
+                minRepetition = maxRepetition = 1;
+            } else {
+                patternChar = parsedPattern[ip];
+                switch (patternChar) {
+                    case PARSED_MODIFIER_ZERO_OR_MORE:
+                        minRepetition = 0;
+                        maxRepetition = Integer.MAX_VALUE;
+                        ip++;
+                        break;
+                    case PARSED_MODIFIER_ONE_OR_MORE:
+                        minRepetition = 1;
+                        maxRepetition = Integer.MAX_VALUE;
+                        ip++;
+                        break;
+                    case PARSED_MODIFIER_RANGE_START:
+                        minRepetition = parsedPattern[++ip];
+                        maxRepetition = parsedPattern[++ip];
+                        ip += 2; // step over PARSED_MODIFIER_RANGE_STOP and on to the next token
+                        break;
+                    default:
+                        minRepetition = maxRepetition = 1; // implied literal
+                        break;
+                }
+            }
+            if (minRepetition > maxRepetition) {
+                return false;
+            }
+
+            // attempt to match as many characters as possible
+            int matched = matchChars(match, im, LM, tokenType, minRepetition, maxRepetition,
+                    parsedPattern, charSetStart, charSetEnd);
+
+            // if we found a conflict, return false immediately
+            if (matched == NO_MATCH) {
+                return false;
+            }
+
+            // move the match pointer the number of characters matched
+            im += matched;
+        }
+        return ip >= LP && im >= LM; // have parsed entire string and regex
+    }
+
+    private static int matchChars(String match, int im, final int lm, int tokenType,
+            int minRepetition, int maxRepetition, int[] parsedPattern,
+            int tokenStart, int tokenEnd) {
+        int matched = 0;
+
+        while(matched < maxRepetition
+                && matchChar(match, im + matched, lm, tokenType, parsedPattern, tokenStart,
+                    tokenEnd)) {
+            matched++;
+        }
+
+        return matched < minRepetition ? NO_MATCH : matched;
+    }
+
+    private static boolean matchChar(String match, int im, final int lm, int tokenType,
+            int[] parsedPattern, int tokenStart, int tokenEnd) {
+        if (im >= lm) { // we've overrun the string, no match
+            return false;
+        }
+        switch (tokenType) {
+            case TOKEN_TYPE_ANY:
+                return true;
+            case TOKEN_TYPE_SET:
+                for (int i = tokenStart; i < tokenEnd; i += 2) {
+                    char matchChar = match.charAt(im);
+                    if (matchChar >= parsedPattern[i] && matchChar <= parsedPattern[i + 1]) {
+                        return true;
+                    }
+                }
+                return false;
+            case TOKEN_TYPE_INVERSE_SET:
+                for (int i = tokenStart; i < tokenEnd; i += 2) {
+                    char matchChar = match.charAt(im);
+                    if (matchChar >= parsedPattern[i] && matchChar <= parsedPattern[i + 1]) {
+                        return false;
+                    }
+                }
+                return true;
+            case TOKEN_TYPE_LITERAL:
+                return match.charAt(im) == parsedPattern[tokenStart];
+            default:
+                return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/android-34/android/os/PerformanceCollector.java b/android-34/android/os/PerformanceCollector.java
new file mode 100644
index 0000000..e6471ae
--- /dev/null
+++ b/android-34/android/os/PerformanceCollector.java
@@ -0,0 +1,594 @@
+/*
+ * 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.
+ * 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.os;
+
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import java.util.ArrayList;
+
+/**
+ * Collects performance data between two function calls in Bundle objects and
+ * outputs the results using writer of type {@link PerformanceResultsWriter}.
+ * <p>
+ * {@link #beginSnapshot(String)} and {@link #endSnapshot()} functions collect
+ * memory usage information and measure runtime between calls to begin and end.
+ * These functions logically wrap around an entire test, and should be called
+ * with name of test as the label, e.g. EmailPerformanceTest.
+ * <p>
+ * {@link #startTiming(String)} and {@link #stopTiming(String)} functions
+ * measure runtime between calls to start and stop. These functions logically
+ * wrap around a single test case or a small block of code, and should be called
+ * with the name of test case as the label, e.g. testSimpleSendMailSequence.
+ * <p>
+ * {@link #addIteration(String)} inserts intermediate measurement point which
+ * can be labeled with a String, e.g. Launch email app, compose, send, etc.
+ * <p>
+ * Snapshot and timing functions do not interfere with each other, and thus can
+ * be called in any order. The intended structure is to wrap begin/endSnapshot
+ * around calls to start/stopTiming, for example:
+ * <p>
+ * <code>beginSnapshot("EmailPerformanceTest");
+ * startTiming("testSimpleSendSequence");
+ * addIteration("Launch email app");
+ * addIteration("Compose");
+ * stopTiming("Send");
+ * startTiming("testComplexSendSequence");
+ * stopTiming("");
+ * startTiming("testAddLabel");
+ * stopTiming("");
+ * endSnapshot();</code>
+ * <p>
+ * Structure of results output is up to implementor of
+ * {@link PerformanceResultsWriter }.
+ *
+ * {@hide} Pending approval for public API.
+ */
+public class PerformanceCollector {
+
+    /**
+     * Interface for reporting performance data.
+     */
+    public interface PerformanceResultsWriter {
+
+        /**
+         * Callback invoked as first action in
+         * PerformanceCollector#beginSnapshot(String) for reporting the start of
+         * a performance snapshot.
+         *
+         * @param label description of code block between beginSnapshot and
+         *              PerformanceCollector#endSnapshot()
+         * @see PerformanceCollector#beginSnapshot(String)
+         */
+        public void writeBeginSnapshot(String label);
+
+        /**
+         * Callback invoked as last action in PerformanceCollector#endSnapshot()
+         * for reporting performance data collected in the snapshot.
+         *
+         * @param results memory and runtime metrics stored as key/value pairs,
+         *        in the same structure as returned by
+         *        PerformanceCollector#endSnapshot()
+         * @see PerformanceCollector#endSnapshot()
+         */
+        public void writeEndSnapshot(Bundle results);
+
+        /**
+         * Callback invoked as first action in
+         * PerformanceCollector#startTiming(String) for reporting the start of
+         * a timing measurement.
+         *
+         * @param label description of code block between startTiming and
+         *              PerformanceCollector#stopTiming(String)
+         * @see PerformanceCollector#startTiming(String)
+         */
+        public void writeStartTiming(String label);
+
+        /**
+         * Callback invoked as last action in
+         * {@link PerformanceCollector#stopTiming(String)} for reporting the
+         * sequence of timings measured.
+         *
+         * @param results runtime metrics of code block between calls to
+         *                startTiming and stopTiming, in the same structure as
+         *                returned by PerformanceCollector#stopTiming(String)
+         * @see PerformanceCollector#stopTiming(String)
+         */
+        public void writeStopTiming(Bundle results);
+
+        /**
+         * Callback invoked as last action in
+         * {@link PerformanceCollector#addMeasurement(String, long)} for
+         * reporting an integer type measurement.
+         *
+         * @param label short description of the metric that was measured
+         * @param value long value of the measurement
+         */
+        public void writeMeasurement(String label, long value);
+
+        /**
+         * Callback invoked as last action in
+         * {@link PerformanceCollector#addMeasurement(String, float)} for
+         * reporting a float type measurement.
+         *
+         * @param label short description of the metric that was measured
+         * @param value float value of the measurement
+         */
+        public void writeMeasurement(String label, float value);
+
+        /**
+         * Callback invoked as last action in
+         * {@link PerformanceCollector#addMeasurement(String, String)} for
+         * reporting a string field.
+         *
+         * @param label short description of the metric that was measured
+         * @param value string summary of the measurement
+         */
+        public void writeMeasurement(String label, String value);
+    }
+
+    /**
+     * In a results Bundle, this key references a List of iteration Bundles.
+     */
+    public static final String METRIC_KEY_ITERATIONS = "iterations";
+    /**
+     * In an iteration Bundle, this key describes the iteration.
+     */
+    public static final String METRIC_KEY_LABEL = "label";
+    /**
+     * In a results Bundle, this key reports the cpu time of the code block
+     * under measurement.
+     */
+    public static final String METRIC_KEY_CPU_TIME = "cpu_time";
+    /**
+     * In a results Bundle, this key reports the execution time of the code
+     * block under measurement.
+     */
+    public static final String METRIC_KEY_EXECUTION_TIME = "execution_time";
+    /**
+     * In a snapshot Bundle, this key reports the number of received
+     * transactions from the binder driver before collection started.
+     */
+    public static final String METRIC_KEY_PRE_RECEIVED_TRANSACTIONS = "pre_received_transactions";
+    /**
+     * In a snapshot Bundle, this key reports the number of transactions sent by
+     * the running program before collection started.
+     */
+    public static final String METRIC_KEY_PRE_SENT_TRANSACTIONS = "pre_sent_transactions";
+    /**
+     * In a snapshot Bundle, this key reports the number of received
+     * transactions from the binder driver.
+     */
+    public static final String METRIC_KEY_RECEIVED_TRANSACTIONS = "received_transactions";
+    /**
+     * In a snapshot Bundle, this key reports the number of transactions sent by
+     * the running program.
+     */
+    public static final String METRIC_KEY_SENT_TRANSACTIONS = "sent_transactions";
+    /**
+     * In a snapshot Bundle, this key reports the number of garbage collection
+     * invocations.
+     */
+    public static final String METRIC_KEY_GC_INVOCATION_COUNT = "gc_invocation_count";
+    /**
+     * In a snapshot Bundle, this key reports the amount of allocated memory
+     * used by the running program.
+     */
+    public static final String METRIC_KEY_JAVA_ALLOCATED = "java_allocated";
+    /**
+     * In a snapshot Bundle, this key reports the amount of free memory
+     * available to the running program.
+     */
+    public static final String METRIC_KEY_JAVA_FREE = "java_free";
+    /**
+     * In a snapshot Bundle, this key reports the number of private dirty pages
+     * used by dalvik.
+     */
+    public static final String METRIC_KEY_JAVA_PRIVATE_DIRTY = "java_private_dirty";
+    /**
+     * In a snapshot Bundle, this key reports the proportional set size for
+     * dalvik.
+     */
+    public static final String METRIC_KEY_JAVA_PSS = "java_pss";
+    /**
+     * In a snapshot Bundle, this key reports the number of shared dirty pages
+     * used by dalvik.
+     */
+    public static final String METRIC_KEY_JAVA_SHARED_DIRTY = "java_shared_dirty";
+    /**
+     * In a snapshot Bundle, this key reports the total amount of memory
+     * available to the running program.
+     */
+    public static final String METRIC_KEY_JAVA_SIZE = "java_size";
+    /**
+     * In a snapshot Bundle, this key reports the amount of allocated memory in
+     * the native heap.
+     */
+    public static final String METRIC_KEY_NATIVE_ALLOCATED = "native_allocated";
+    /**
+     * In a snapshot Bundle, this key reports the amount of free memory in the
+     * native heap.
+     */
+    public static final String METRIC_KEY_NATIVE_FREE = "native_free";
+    /**
+     * In a snapshot Bundle, this key reports the number of private dirty pages
+     * used by the native heap.
+     */
+    public static final String METRIC_KEY_NATIVE_PRIVATE_DIRTY = "native_private_dirty";
+    /**
+     * In a snapshot Bundle, this key reports the proportional set size for the
+     * native heap.
+     */
+    public static final String METRIC_KEY_NATIVE_PSS = "native_pss";
+    /**
+     * In a snapshot Bundle, this key reports the number of shared dirty pages
+     * used by the native heap.
+     */
+    public static final String METRIC_KEY_NATIVE_SHARED_DIRTY = "native_shared_dirty";
+    /**
+     * In a snapshot Bundle, this key reports the size of the native heap.
+     */
+    public static final String METRIC_KEY_NATIVE_SIZE = "native_size";
+    /**
+     * In a snapshot Bundle, this key reports the number of objects allocated
+     * globally.
+     */
+    public static final String METRIC_KEY_GLOBAL_ALLOC_COUNT = "global_alloc_count";
+    /**
+     * In a snapshot Bundle, this key reports the size of all objects allocated
+     * globally.
+     */
+    public static final String METRIC_KEY_GLOBAL_ALLOC_SIZE = "global_alloc_size";
+    /**
+     * In a snapshot Bundle, this key reports the number of objects freed
+     * globally.
+     */
+    public static final String METRIC_KEY_GLOBAL_FREED_COUNT = "global_freed_count";
+    /**
+     * In a snapshot Bundle, this key reports the size of all objects freed
+     * globally.
+     */
+    public static final String METRIC_KEY_GLOBAL_FREED_SIZE = "global_freed_size";
+    /**
+     * In a snapshot Bundle, this key reports the number of private dirty pages
+     * used by everything else.
+     */
+    public static final String METRIC_KEY_OTHER_PRIVATE_DIRTY = "other_private_dirty";
+    /**
+     * In a snapshot Bundle, this key reports the proportional set size for
+     * everything else.
+     */
+    public static final String METRIC_KEY_OTHER_PSS = "other_pss";
+    /**
+     * In a snapshot Bundle, this key reports the number of shared dirty pages
+     * used by everything else.
+     */
+    public static final String METRIC_KEY_OTHER_SHARED_DIRTY = "other_shared_dirty";
+
+    private PerformanceResultsWriter mPerfWriter;
+    private Bundle mPerfSnapshot;
+    private Bundle mPerfMeasurement;
+    private long mSnapshotCpuTime;
+    private long mSnapshotExecTime;
+    private long mCpuTime;
+    private long mExecTime;
+
+    @UnsupportedAppUsage
+    public PerformanceCollector() {
+    }
+
+    public PerformanceCollector(PerformanceResultsWriter writer) {
+        setPerformanceResultsWriter(writer);
+    }
+
+    public void setPerformanceResultsWriter(PerformanceResultsWriter writer) {
+        mPerfWriter = writer;
+    }
+
+    /**
+     * Begin collection of memory usage information.
+     *
+     * @param label description of code block between beginSnapshot and
+     *              endSnapshot, used to label output
+     */
+    @UnsupportedAppUsage
+    public void beginSnapshot(String label) {
+        if (mPerfWriter != null)
+            mPerfWriter.writeBeginSnapshot(label);
+        startPerformanceSnapshot();
+    }
+
+    /**
+     * End collection of memory usage information. Returns collected data in a
+     * Bundle object.
+     *
+     * @return Memory and runtime metrics stored as key/value pairs. Values are
+     *         of type long, and keys include:
+     *         <ul>
+     *         <li>{@link #METRIC_KEY_CPU_TIME cpu_time}
+     *         <li>{@link #METRIC_KEY_EXECUTION_TIME execution_time}
+     *         <li>{@link #METRIC_KEY_PRE_RECEIVED_TRANSACTIONS
+     *         pre_received_transactions}
+     *         <li>{@link #METRIC_KEY_PRE_SENT_TRANSACTIONS
+     *         pre_sent_transactions}
+     *         <li>{@link #METRIC_KEY_RECEIVED_TRANSACTIONS
+     *         received_transactions}
+     *         <li>{@link #METRIC_KEY_SENT_TRANSACTIONS sent_transactions}
+     *         <li>{@link #METRIC_KEY_GC_INVOCATION_COUNT gc_invocation_count}
+     *         <li>{@link #METRIC_KEY_JAVA_ALLOCATED java_allocated}
+     *         <li>{@link #METRIC_KEY_JAVA_FREE java_free}
+     *         <li>{@link #METRIC_KEY_JAVA_PRIVATE_DIRTY java_private_dirty}
+     *         <li>{@link #METRIC_KEY_JAVA_PSS java_pss}
+     *         <li>{@link #METRIC_KEY_JAVA_SHARED_DIRTY java_shared_dirty}
+     *         <li>{@link #METRIC_KEY_JAVA_SIZE java_size}
+     *         <li>{@link #METRIC_KEY_NATIVE_ALLOCATED native_allocated}
+     *         <li>{@link #METRIC_KEY_NATIVE_FREE native_free}
+     *         <li>{@link #METRIC_KEY_NATIVE_PRIVATE_DIRTY native_private_dirty}
+     *         <li>{@link #METRIC_KEY_NATIVE_PSS native_pss}
+     *         <li>{@link #METRIC_KEY_NATIVE_SHARED_DIRTY native_shared_dirty}
+     *         <li>{@link #METRIC_KEY_NATIVE_SIZE native_size}
+     *         <li>{@link #METRIC_KEY_GLOBAL_ALLOC_COUNT global_alloc_count}
+     *         <li>{@link #METRIC_KEY_GLOBAL_ALLOC_SIZE global_alloc_size}
+     *         <li>{@link #METRIC_KEY_GLOBAL_FREED_COUNT global_freed_count}
+     *         <li>{@link #METRIC_KEY_GLOBAL_FREED_SIZE global_freed_size}
+     *         <li>{@link #METRIC_KEY_OTHER_PRIVATE_DIRTY other_private_dirty}
+     *         <li>{@link #METRIC_KEY_OTHER_PSS other_pss}
+     *         <li>{@link #METRIC_KEY_OTHER_SHARED_DIRTY other_shared_dirty}
+     *         </ul>
+     */
+    @UnsupportedAppUsage
+    public Bundle endSnapshot() {
+        endPerformanceSnapshot();
+        if (mPerfWriter != null)
+            mPerfWriter.writeEndSnapshot(mPerfSnapshot);
+        return mPerfSnapshot;
+    }
+
+    /**
+     * Start measurement of user and cpu time.
+     *
+     * @param label description of code block between startTiming and
+     *        stopTiming, used to label output
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void startTiming(String label) {
+        if (mPerfWriter != null)
+            mPerfWriter.writeStartTiming(label);
+        mPerfMeasurement = new Bundle();
+        mPerfMeasurement.putParcelableArrayList(
+                METRIC_KEY_ITERATIONS, new ArrayList<Parcelable>());
+        mExecTime = SystemClock.uptimeMillis();
+        mCpuTime = Process.getElapsedCpuTime();
+    }
+
+    /**
+     * Add a measured segment, and start measuring the next segment. Returns
+     * collected data in a Bundle object.
+     *
+     * @param label description of code block between startTiming and
+     *              addIteration, and between two calls to addIteration, used
+     *              to label output
+     * @return Runtime metrics stored as key/value pairs. Values are of type
+     *         long, and keys include:
+     *         <ul>
+     *         <li>{@link #METRIC_KEY_LABEL label}
+     *         <li>{@link #METRIC_KEY_CPU_TIME cpu_time}
+     *         <li>{@link #METRIC_KEY_EXECUTION_TIME execution_time}
+     *         </ul>
+     */
+    public Bundle addIteration(String label) {
+        mCpuTime = Process.getElapsedCpuTime() - mCpuTime;
+        mExecTime = SystemClock.uptimeMillis() - mExecTime;
+
+        Bundle iteration = new Bundle();
+        iteration.putString(METRIC_KEY_LABEL, label);
+        iteration.putLong(METRIC_KEY_EXECUTION_TIME, mExecTime);
+        iteration.putLong(METRIC_KEY_CPU_TIME, mCpuTime);
+        mPerfMeasurement.getParcelableArrayList(METRIC_KEY_ITERATIONS).add(iteration);
+
+        mExecTime = SystemClock.uptimeMillis();
+        mCpuTime = Process.getElapsedCpuTime();
+        return iteration;
+    }
+
+    /**
+     * Stop measurement of user and cpu time.
+     *
+     * @param label description of code block between addIteration or
+     *              startTiming and stopTiming, used to label output
+     * @return Runtime metrics stored in a bundle, including all iterations
+     *         between calls to startTiming and stopTiming. List of iterations
+     *         is keyed by {@link #METRIC_KEY_ITERATIONS iterations}.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public Bundle stopTiming(String label) {
+        addIteration(label);
+        if (mPerfWriter != null)
+            mPerfWriter.writeStopTiming(mPerfMeasurement);
+        return mPerfMeasurement;
+    }
+
+    /**
+     * Add an integer type measurement to the collector.
+     *
+     * @param label short description of the metric that was measured
+     * @param value long value of the measurement
+     */
+    public void addMeasurement(String label, long value) {
+        if (mPerfWriter != null)
+            mPerfWriter.writeMeasurement(label, value);
+    }
+
+    /**
+     * Add a float type measurement to the collector.
+     *
+     * @param label short description of the metric that was measured
+     * @param value float value of the measurement
+     */
+    public void addMeasurement(String label, float value) {
+        if (mPerfWriter != null)
+            mPerfWriter.writeMeasurement(label, value);
+    }
+
+    /**
+     * Add a string field to the collector.
+     *
+     * @param label short description of the metric that was measured
+     * @param value string summary of the measurement
+     */
+    public void addMeasurement(String label, String value) {
+        if (mPerfWriter != null)
+            mPerfWriter.writeMeasurement(label, value);
+    }
+
+    /*
+     * Starts tracking memory usage, binder transactions, and real & cpu timing.
+     */
+    private void startPerformanceSnapshot() {
+        // Create new snapshot
+        mPerfSnapshot = new Bundle();
+
+        // Add initial binder counts
+        Bundle binderCounts = getBinderCounts();
+        for (String key : binderCounts.keySet()) {
+            mPerfSnapshot.putLong("pre_" + key, binderCounts.getLong(key));
+        }
+
+        // Force a GC and zero out the performance counters. Do this
+        // before reading initial CPU/wall-clock times so we don't include
+        // the cost of this setup in our final metrics.
+        startAllocCounting();
+
+        // Record CPU time up to this point, and start timing. Note: this
+        // must happen at the end of this method, otherwise the timing will
+        // include noise.
+        mSnapshotExecTime = SystemClock.uptimeMillis();
+        mSnapshotCpuTime = Process.getElapsedCpuTime();
+    }
+
+    /*
+     * Stops tracking memory usage, binder transactions, and real & cpu timing.
+     * Stores collected data as type long into Bundle object for reporting.
+     */
+    private void endPerformanceSnapshot() {
+        // Stop the timing. This must be done first before any other counting is
+        // stopped.
+        mSnapshotCpuTime = Process.getElapsedCpuTime() - mSnapshotCpuTime;
+        mSnapshotExecTime = SystemClock.uptimeMillis() - mSnapshotExecTime;
+
+        stopAllocCounting();
+
+        long nativeMax = Debug.getNativeHeapSize() / 1024;
+        long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
+        long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
+
+        Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
+        Debug.getMemoryInfo(memInfo);
+
+        Runtime runtime = Runtime.getRuntime();
+
+        long dalvikMax = runtime.totalMemory() / 1024;
+        long dalvikFree = runtime.freeMemory() / 1024;
+        long dalvikAllocated = dalvikMax - dalvikFree;
+
+        // Add final binder counts
+        Bundle binderCounts = getBinderCounts();
+        for (String key : binderCounts.keySet()) {
+            mPerfSnapshot.putLong(key, binderCounts.getLong(key));
+        }
+
+        // Add alloc counts
+        Bundle allocCounts = getAllocCounts();
+        for (String key : allocCounts.keySet()) {
+            mPerfSnapshot.putLong(key, allocCounts.getLong(key));
+        }
+
+        mPerfSnapshot.putLong(METRIC_KEY_EXECUTION_TIME, mSnapshotExecTime);
+        mPerfSnapshot.putLong(METRIC_KEY_CPU_TIME, mSnapshotCpuTime);
+
+        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_SIZE, nativeMax);
+        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_ALLOCATED, nativeAllocated);
+        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_FREE, nativeFree);
+        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_PSS, memInfo.nativePss);
+        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_PRIVATE_DIRTY, memInfo.nativePrivateDirty);
+        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_SHARED_DIRTY, memInfo.nativeSharedDirty);
+
+        mPerfSnapshot.putLong(METRIC_KEY_JAVA_SIZE, dalvikMax);
+        mPerfSnapshot.putLong(METRIC_KEY_JAVA_ALLOCATED, dalvikAllocated);
+        mPerfSnapshot.putLong(METRIC_KEY_JAVA_FREE, dalvikFree);
+        mPerfSnapshot.putLong(METRIC_KEY_JAVA_PSS, memInfo.dalvikPss);
+        mPerfSnapshot.putLong(METRIC_KEY_JAVA_PRIVATE_DIRTY, memInfo.dalvikPrivateDirty);
+        mPerfSnapshot.putLong(METRIC_KEY_JAVA_SHARED_DIRTY, memInfo.dalvikSharedDirty);
+
+        mPerfSnapshot.putLong(METRIC_KEY_OTHER_PSS, memInfo.otherPss);
+        mPerfSnapshot.putLong(METRIC_KEY_OTHER_PRIVATE_DIRTY, memInfo.otherPrivateDirty);
+        mPerfSnapshot.putLong(METRIC_KEY_OTHER_SHARED_DIRTY, memInfo.otherSharedDirty);
+    }
+
+    /*
+     * Starts allocation counting. This triggers a gc and resets the counts.
+     */
+    private static void startAllocCounting() {
+        // Before we start trigger a GC and reset the debug counts. Run the
+        // finalizers and another GC before starting and stopping the alloc
+        // counts. This will free up any objects that were just sitting around
+        // waiting for their finalizers to be run.
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+
+        Debug.resetAllCounts();
+
+        // start the counts
+        Debug.startAllocCounting();
+    }
+
+    /*
+     * Stops allocation counting.
+     */
+    private static void stopAllocCounting() {
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+        Debug.stopAllocCounting();
+    }
+
+    /*
+     * Returns a bundle with the current results from the allocation counting.
+     */
+    private static Bundle getAllocCounts() {
+        Bundle results = new Bundle();
+        results.putLong(METRIC_KEY_GLOBAL_ALLOC_COUNT, Debug.getGlobalAllocCount());
+        results.putLong(METRIC_KEY_GLOBAL_ALLOC_SIZE, Debug.getGlobalAllocSize());
+        results.putLong(METRIC_KEY_GLOBAL_FREED_COUNT, Debug.getGlobalFreedCount());
+        results.putLong(METRIC_KEY_GLOBAL_FREED_SIZE, Debug.getGlobalFreedSize());
+        results.putLong(METRIC_KEY_GC_INVOCATION_COUNT, Debug.getGlobalGcInvocationCount());
+        return results;
+    }
+
+    /*
+     * Returns a bundle with the counts for various binder counts for this
+     * process. Currently the only two that are reported are the number of send
+     * and the number of received transactions.
+     */
+    private static Bundle getBinderCounts() {
+        Bundle results = new Bundle();
+        results.putLong(METRIC_KEY_SENT_TRANSACTIONS, Debug.getBinderSentTransactions());
+        results.putLong(METRIC_KEY_RECEIVED_TRANSACTIONS, Debug.getBinderReceivedTransactions());
+        return results;
+    }
+}
diff --git a/android-34/android/os/PerformanceHintManager.java b/android-34/android/os/PerformanceHintManager.java
new file mode 100644
index 0000000..f79d6e6
--- /dev/null
+++ b/android-34/android/os/PerformanceHintManager.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.Closeable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Reference;
+
+
+/** The PerformanceHintManager allows apps to send performance hint to system. */
+@SystemService(Context.PERFORMANCE_HINT_SERVICE)
+public final class PerformanceHintManager {
+    private final long mNativeManagerPtr;
+
+    /** @hide */
+    public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException {
+        long nativeManagerPtr = nativeAcquireManager();
+        if (nativeManagerPtr == 0) {
+            throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE);
+        }
+        return new PerformanceHintManager(nativeManagerPtr);
+    }
+
+    private PerformanceHintManager(long nativeManagerPtr) {
+        mNativeManagerPtr = nativeManagerPtr;
+    }
+
+    /**
+     * Creates a {@link Session} for the given set of threads and sets their initial target work
+     * duration.
+     *
+     * @param tids The list of threads to be associated with this session. They must be part of
+     *     this process' thread group.
+     * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new
+     *     session.
+     * @return the new session if it is supported on this device, null if hint session is not
+     *     supported on this device.
+     */
+    @Nullable
+    public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) {
+        Preconditions.checkNotNull(tids, "tids cannot be null");
+        Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos,
+                "the hint target duration should be positive.");
+        long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids,
+                initialTargetWorkDurationNanos);
+        if (nativeSessionPtr == 0) return null;
+        return new Session(nativeSessionPtr);
+    }
+
+    /**
+     * Get preferred update rate information for this device.
+     *
+     * @return the preferred update rate supported by device software.
+     */
+    public long getPreferredUpdateRateNanos() {
+        return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr);
+    }
+
+    /**
+     * A Session represents a group of threads with an inter-related workload such that hints for
+     * their performance should be considered as a unit. The threads in a given session should be
+     * long-life and not created or destroyed dynamically.
+     *
+     * <p>Each session is expected to have a periodic workload with a target duration for each
+     * cycle. The cycle duration is likely greater than the target work duration to allow other
+     * parts of the pipeline to run within the available budget. For example, a renderer thread may
+     * work at 60hz in order to produce frames at the display's frame but have a target work
+     * duration of only 6ms.</p>
+     *
+     * <p>Any call in this class will change its internal data, so you must do your own thread
+     * safety to protect from racing.</p>
+     *
+     * <p>Note that the target work duration can be {@link #updateTargetWorkDuration(long) updated}
+     * if workloads change.</p>
+     *
+     * <p>After each cycle of work, the client is expected to
+     * {@link #reportActualWorkDuration(long) report} the actual time taken to complete.</p>
+     *
+     * <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p>
+     */
+    public static class Session implements Closeable {
+        private long mNativeSessionPtr;
+
+        /** @hide */
+        public Session(long nativeSessionPtr) {
+            mNativeSessionPtr = nativeSessionPtr;
+        }
+
+        /**
+        * This hint indicates a sudden increase in CPU workload intensity. It means
+        * that this hint session needs extra CPU resources immediately to meet the
+        * target duration for the current work cycle.
+        *
+        * @hide
+        */
+        @TestApi
+        public static final int CPU_LOAD_UP = 0;
+        /**
+        * This hint indicates a decrease in CPU workload intensity. It means that
+        * this hint session can reduce CPU resources and still meet the target duration.
+        *
+        * @hide
+        */
+        @TestApi
+        public static final int CPU_LOAD_DOWN = 1;
+        /**
+        * This hint indicates an upcoming CPU workload that is completely changed and
+        * unknown. It means that the hint session should reset CPU resources to a known
+        * baseline to prepare for an arbitrary load, and must wake up if inactive.
+        *
+        * @hide
+        */
+        @TestApi
+        public static final int CPU_LOAD_RESET = 2;
+        /**
+        * This hint indicates that the most recent CPU workload is resuming after a
+        * period of inactivity. It means that the hint session should allocate similar
+        * CPU resources to what was used previously, and must wake up if inactive.
+        *
+        * @hide
+        */
+        @TestApi
+        public static final int CPU_LOAD_RESUME = 3;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"CPU_LOAD_"}, value = {
+            CPU_LOAD_UP,
+            CPU_LOAD_DOWN,
+            CPU_LOAD_RESET,
+            CPU_LOAD_RESUME
+        })
+        public @interface Hint {}
+
+        /** @hide */
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
+
+        /**
+         * Updates this session's target duration for each cycle of work.
+         *
+         * @param targetDurationNanos the new desired duration in nanoseconds
+         */
+        public void updateTargetWorkDuration(long targetDurationNanos) {
+            Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration"
+                    + " should be positive.");
+            nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos);
+        }
+
+        /**
+         * Reports the actual duration for the last cycle of work.
+         *
+         * <p>The system will attempt to adjust the core placement of the threads within the thread
+         * group and/or the frequency of the core on which they are run to bring the actual duration
+         * close to the target duration.</p>
+         *
+         * @param actualDurationNanos how long the thread group took to complete its last task in
+         *     nanoseconds
+         */
+        public void reportActualWorkDuration(long actualDurationNanos) {
+            Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should"
+                    + " be positive.");
+            nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos);
+        }
+
+        /**
+         * Ends the current hint session.
+         *
+         * <p>Once called, you should not call anything else on this object.</p>
+         */
+        public void close() {
+            if (mNativeSessionPtr != 0) {
+                nativeCloseSession(mNativeSessionPtr);
+                mNativeSessionPtr = 0;
+            }
+        }
+
+        /**
+         * Sends performance hints to inform the hint session of changes in the workload.
+         *
+         * @param hint The hint to send to the session.
+         *
+         * @hide
+         */
+        @TestApi
+        public void sendHint(@Hint int hint) {
+            Preconditions.checkArgumentNonNegative(hint, "the hint ID should be at least"
+                    + " zero.");
+            try {
+                nativeSendHint(mNativeSessionPtr, hint);
+            } finally {
+                Reference.reachabilityFence(this);
+            }
+        }
+
+        /**
+         * Set a list of threads to the performance hint session. This operation will replace
+         * the current list of threads with the given list of threads.
+         * Note that this is not an oneway method.
+         *
+         * @param tids The list of threads to be associated with this session. They must be
+         *     part of this app's thread group.
+         *
+         * @throws IllegalStateException if the hint session is not in the foreground.
+         * @throws IllegalArgumentException if the thread id list is empty.
+         * @throws SecurityException if any thread id doesn't belong to the application.
+         */
+        public void setThreads(@NonNull int[] tids) {
+            if (mNativeSessionPtr == 0) {
+                return;
+            }
+            if (tids.length == 0) {
+                throw new IllegalArgumentException("Thread id list can't be empty.");
+            }
+            nativeSetThreads(mNativeSessionPtr, tids);
+        }
+
+        /**
+         * Returns the list of thread ids.
+         *
+         * @hide
+         */
+        @TestApi
+        public @Nullable int[] getThreadIds() {
+            return nativeGetThreadIds(mNativeSessionPtr);
+        }
+    }
+
+    private static native long nativeAcquireManager();
+    private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr);
+    private static native long nativeCreateSession(long nativeManagerPtr,
+            int[] tids, long initialTargetWorkDurationNanos);
+    private static native int[] nativeGetThreadIds(long nativeSessionPtr);
+    private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr,
+            long targetDurationNanos);
+    private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
+            long actualDurationNanos);
+    private static native void nativeCloseSession(long nativeSessionPtr);
+    private static native void nativeSendHint(long nativeSessionPtr, int hint);
+    private static native void nativeSetThreads(long nativeSessionPtr, int[] tids);
+}
diff --git a/android-34/android/os/PermissionEnforcer.java b/android-34/android/os/PermissionEnforcer.java
new file mode 100644
index 0000000..310ceb3
--- /dev/null
+++ b/android-34/android/os/PermissionEnforcer.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2022 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.app.AppOpsManager;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.content.PermissionChecker;
+import android.content.pm.PackageManager;
+import android.permission.PermissionCheckerManager;
+
+/**
+ * PermissionEnforcer check permissions for AIDL-generated services which use
+ * the @EnforcePermission annotation.
+ *
+ * <p>AIDL services may be annotated with @EnforcePermission which will trigger
+ * the generation of permission check code. This generated code relies on
+ * PermissionEnforcer to validate the permissions. The methods available are
+ * purposely similar to the AIDL annotation syntax.
+ *
+ * @see android.permission.PermissionManager
+ *
+ * @hide
+ */
+@SystemService(Context.PERMISSION_ENFORCER_SERVICE)
+public class PermissionEnforcer {
+
+    private final Context mContext;
+    private static final String ACCESS_DENIED = "Access denied, requires: ";
+
+    /** Protected constructor. Allows subclasses to instantiate an object
+     *  without using a Context.
+     */
+    protected PermissionEnforcer() {
+        mContext = null;
+    }
+
+    /** Constructor, prefer using the fromContext static method when possible */
+    public PermissionEnforcer(@NonNull Context context) {
+        mContext = context;
+    }
+
+    @PermissionCheckerManager.PermissionResult
+    protected int checkPermission(@NonNull String permission, @NonNull AttributionSource source) {
+        return PermissionChecker.checkPermissionForDataDelivery(
+            mContext, permission, PermissionChecker.PID_UNKNOWN, source, "" /* message */);
+    }
+
+    @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
+    @PermissionCheckerManager.PermissionResult
+    protected int checkPermission(@NonNull String permission, int pid, int uid) {
+        if (mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
+            return PermissionCheckerManager.PERMISSION_GRANTED;
+        }
+        return PermissionCheckerManager.PERMISSION_HARD_DENIED;
+    }
+
+    private boolean anyAppOps(@NonNull String[] permissions) {
+        for (String permission : permissions) {
+            if (AppOpsManager.permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void enforcePermission(@NonNull String permission, @NonNull
+            AttributionSource source) throws SecurityException {
+        int result = checkPermission(permission, source);
+        if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
+            throw new SecurityException(ACCESS_DENIED + permission);
+        }
+    }
+
+    public void enforcePermission(@NonNull String permission, int pid, int uid)
+            throws SecurityException {
+        if (AppOpsManager.permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
+            AttributionSource source = new AttributionSource(uid, null, null);
+            enforcePermission(permission, source);
+            return;
+        }
+        int result = checkPermission(permission, pid, uid);
+        if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
+            throw new SecurityException(ACCESS_DENIED + permission);
+        }
+    }
+
+    public void enforcePermissionAllOf(@NonNull String[] permissions,
+            @NonNull AttributionSource source) throws SecurityException {
+        for (String permission : permissions) {
+            int result = checkPermission(permission, source);
+            if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
+                throw new SecurityException(ACCESS_DENIED + "allOf={"
+                        + String.join(", ", permissions) + "}");
+            }
+        }
+    }
+
+    public void enforcePermissionAllOf(@NonNull String[] permissions,
+            int pid, int uid) throws SecurityException {
+        if (anyAppOps(permissions)) {
+            AttributionSource source = new AttributionSource(uid, null, null);
+            enforcePermissionAllOf(permissions, source);
+            return;
+        }
+        for (String permission : permissions) {
+            int result = checkPermission(permission, pid, uid);
+            if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
+                throw new SecurityException(ACCESS_DENIED + "allOf={"
+                        + String.join(", ", permissions) + "}");
+            }
+        }
+    }
+
+    public void enforcePermissionAnyOf(@NonNull String[] permissions,
+            @NonNull AttributionSource source) throws SecurityException {
+        for (String permission : permissions) {
+            int result = checkPermission(permission, source);
+            if (result == PermissionCheckerManager.PERMISSION_GRANTED) {
+                return;
+            }
+        }
+        throw new SecurityException(ACCESS_DENIED + "anyOf={"
+                + String.join(", ", permissions) + "}");
+    }
+
+    public void enforcePermissionAnyOf(@NonNull String[] permissions,
+            int pid, int uid) throws SecurityException {
+        if (anyAppOps(permissions)) {
+            AttributionSource source = new AttributionSource(uid, null, null);
+            enforcePermissionAnyOf(permissions, source);
+            return;
+        }
+        for (String permission : permissions) {
+            int result = checkPermission(permission, pid, uid);
+            if (result == PermissionCheckerManager.PERMISSION_GRANTED) {
+                return;
+            }
+        }
+        throw new SecurityException(ACCESS_DENIED + "anyOf={"
+                + String.join(", ", permissions) + "}");
+    }
+
+    /**
+     * Returns a new PermissionEnforcer based on a Context.
+     *
+     * @hide
+     */
+    public static PermissionEnforcer fromContext(@NonNull Context context) {
+        return context.getSystemService(PermissionEnforcer.class);
+    }
+}
diff --git a/android-34/android/os/PersistableBundle.java b/android-34/android/os/PersistableBundle.java
new file mode 100644
index 0000000..02704f5
--- /dev/null
+++ b/android-34/android/os/PersistableBundle.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2014 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.os;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * A mapping from String keys to values of various types. The set of types
+ * supported by this class is purposefully restricted to simple objects that can
+ * safely be persisted to and restored from disk.
+ *
+ * <p><b>Warning:</b> Note that {@link PersistableBundle} is a lazy container and as such it does
+ * NOT implement {@link #equals(Object)} or {@link #hashCode()}.
+ *
+ * @see Bundle
+ */
+public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
+        XmlUtils.WriteMapCallback {
+    private static final String TAG = "PersistableBundle";
+
+    private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
+
+    /** An unmodifiable {@code PersistableBundle} that is always {@link #isEmpty() empty}. */
+    public static final PersistableBundle EMPTY;
+
+    static {
+        EMPTY = new PersistableBundle();
+        EMPTY.mMap = ArrayMap.EMPTY;
+    }
+
+    /** @hide */
+    public static boolean isValidType(Object value) {
+        return (value instanceof Integer) || (value instanceof Long) ||
+                (value instanceof Double) || (value instanceof String) ||
+                (value instanceof int[]) || (value instanceof long[]) ||
+                (value instanceof double[]) || (value instanceof String[]) ||
+                (value instanceof PersistableBundle) || (value == null) ||
+                (value instanceof Boolean) || (value instanceof boolean[]);
+    }
+
+    /**
+     * Constructs a new, empty PersistableBundle.
+     */
+    public PersistableBundle() {
+        super();
+        mFlags = FLAG_DEFUSABLE;
+    }
+
+    /**
+     * Constructs a new, empty PersistableBundle sized to hold the given number of
+     * elements. The PersistableBundle will grow as needed.
+     *
+     * @param capacity the initial capacity of the PersistableBundle
+     */
+    public PersistableBundle(int capacity) {
+        super(capacity);
+        mFlags = FLAG_DEFUSABLE;
+    }
+
+    /**
+     * Constructs a PersistableBundle containing a copy of the mappings from the given
+     * PersistableBundle.  Does only a shallow copy of the original PersistableBundle -- see
+     * {@link #deepCopy()} if that is not what you want.
+     *
+     * @param b a PersistableBundle to be copied.
+     *
+     * @see #deepCopy()
+     */
+    public PersistableBundle(PersistableBundle b) {
+        super(b);
+        mFlags = b.mFlags;
+    }
+
+
+    /**
+     * Constructs a PersistableBundle from a Bundle.  Does only a shallow copy of the Bundle.
+     *
+     * <p><b>Warning:</b> This method will deserialize every item on the bundle, including custom
+     * types such as {@link Parcelable} and {@link Serializable}, so only use this when you trust
+     * the source. Specifically don't use this method on app-provided bundles.
+     *
+     * @param b a Bundle to be copied.
+     *
+     * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
+     *
+     * @hide
+     */
+    public PersistableBundle(Bundle b) {
+        this(b, true);
+    }
+
+    private PersistableBundle(Bundle b, boolean throwException) {
+        this(b.getItemwiseMap(), throwException);
+    }
+
+    /**
+     * Constructs a PersistableBundle containing the mappings passed in.
+     *
+     * @param map a Map containing only those items that can be persisted.
+     * @throws IllegalArgumentException if any element of #map cannot be persisted.
+     */
+    private PersistableBundle(ArrayMap<String, Object> map, boolean throwException) {
+        super();
+        mFlags = FLAG_DEFUSABLE;
+
+        // First stuff everything in.
+        putAll(map);
+
+        // Now verify each item throwing an exception if there is a violation.
+        final int N = mMap.size();
+        for (int i = N - 1; i >= 0; --i) {
+            Object value = mMap.valueAt(i);
+            if (value instanceof ArrayMap) {
+                // Fix up any Maps by replacing them with PersistableBundles.
+                mMap.setValueAt(i,
+                        new PersistableBundle((ArrayMap<String, Object>) value, throwException));
+            } else if (value instanceof Bundle) {
+                mMap.setValueAt(i, new PersistableBundle((Bundle) value, throwException));
+            } else if (!isValidType(value)) {
+                final String errorMsg = "Bad value in PersistableBundle key="
+                        + mMap.keyAt(i) + " value=" + value;
+                if (throwException) {
+                    throw new IllegalArgumentException(errorMsg);
+                } else {
+                    Slog.wtfStack(TAG, errorMsg);
+                    mMap.removeAt(i);
+                }
+            }
+        }
+    }
+
+    /* package */ PersistableBundle(Parcel parcelledData, int length) {
+        super(parcelledData, length);
+        mFlags = FLAG_DEFUSABLE;
+    }
+
+    /**
+     * Constructs a {@link PersistableBundle} containing a copy of {@code from}.
+     *
+     * @param from The bundle to be copied.
+     * @param deep Whether is a deep or shallow copy.
+     *
+     * @hide
+     */
+    PersistableBundle(PersistableBundle from, boolean deep) {
+        super(from, deep);
+    }
+
+    /**
+     * Make a PersistableBundle for a single key/value pair.
+     *
+     * @hide
+     */
+    public static PersistableBundle forPair(String key, String value) {
+        PersistableBundle b = new PersistableBundle(1);
+        b.putString(key, value);
+        return b;
+    }
+
+    /**
+     * Clones the current PersistableBundle. The internal map is cloned, but the keys and
+     * values to which it refers are copied by reference.
+     */
+    @Override
+    public Object clone() {
+        return new PersistableBundle(this);
+    }
+
+    /**
+     * Make a deep copy of the given bundle.  Traverses into inner containers and copies
+     * them as well, so they are not shared across bundles.  Will traverse in to
+     * {@link Bundle}, {@link PersistableBundle}, {@link ArrayList}, and all types of
+     * primitive arrays.  Other types of objects (such as Parcelable or Serializable)
+     * are referenced as-is and not copied in any way.
+     */
+    public PersistableBundle deepCopy() {
+        return new PersistableBundle(this, /* deep */ true);
+    }
+
+    /**
+     * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Bundle object, or null
+     */
+    public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Bundle value, or null
+     */
+    @Nullable
+    public PersistableBundle getPersistableBundle(@Nullable String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (PersistableBundle) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Bundle", e);
+            return null;
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<PersistableBundle> CREATOR =
+            new Parcelable.Creator<PersistableBundle>() {
+                @Override
+                public PersistableBundle createFromParcel(Parcel in) {
+                    return in.readPersistableBundle();
+                }
+
+                @Override
+                public PersistableBundle[] newArray(int size) {
+                    return new PersistableBundle[size];
+                }
+            };
+
+    /** @hide */
+    @Override
+    public void writeUnknownObject(Object v, String name, TypedXmlSerializer out)
+            throws XmlPullParserException, IOException {
+        if (v instanceof PersistableBundle) {
+            out.startTag(null, TAG_PERSISTABLEMAP);
+            out.attribute(null, "name", name);
+            ((PersistableBundle) v).saveToXml(out);
+            out.endTag(null, TAG_PERSISTABLEMAP);
+        } else {
+            throw new XmlPullParserException("Unknown Object o=" + v);
+        }
+    }
+
+    /** @hide */
+    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        saveToXml(XmlUtils.makeTyped(out));
+    }
+
+    /** @hide */
+    public void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException {
+        unparcel();
+        // Explicitly drop invalid types an attacker may have added before persisting.
+        for (int i = mMap.size() - 1; i >= 0; --i) {
+            final Object value = mMap.valueAt(i);
+            if (!isValidType(value)) {
+                Slog.e(TAG, "Dropping bad data before persisting: "
+                        + mMap.keyAt(i) + "=" + value);
+                mMap.removeAt(i);
+            }
+        }
+        XmlUtils.writeMapXml(mMap, out, this);
+    }
+
+    /** @hide */
+    static class MyReadMapCallback implements  XmlUtils.ReadMapCallback {
+        @Override
+        public Object readThisUnknownObjectXml(TypedXmlPullParser in, String tag)
+                throws XmlPullParserException, IOException {
+            if (TAG_PERSISTABLEMAP.equals(tag)) {
+                return restoreFromXml(in);
+            }
+            throw new XmlPullParserException("Unknown tag=" + tag);
+        }
+    }
+
+    /**
+     * Report the nature of this Parcelable's contents
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes the PersistableBundle contents to a Parcel, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to copy this bundle to.
+     */
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        final boolean oldAllowFds = parcel.pushAllowFds(false);
+        try {
+            writeToParcelInner(parcel, flags);
+        } finally {
+            parcel.restoreAllowFds(oldAllowFds);
+        }
+    }
+
+    /** @hide */
+    public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
+            XmlPullParserException {
+        return restoreFromXml(XmlUtils.makeTyped(in));
+    }
+
+    /** @hide */
+    public static PersistableBundle restoreFromXml(TypedXmlPullParser in) throws IOException,
+            XmlPullParserException {
+        final int outerDepth = in.getDepth();
+        final String startTag = in.getName();
+        final String[] tagName = new String[1];
+        int event;
+        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+            if (event == XmlPullParser.START_TAG) {
+                // Don't throw an exception when restoring from XML since an attacker could try to
+                // input invalid data in the persisted file.
+                return new PersistableBundle((ArrayMap<String, Object>)
+                        XmlUtils.readThisArrayMapXml(in, startTag, tagName,
+                        new MyReadMapCallback()),
+                        /* throwException */ false);
+            }
+        }
+        return new PersistableBundle();  // An empty mutable PersistableBundle
+    }
+
+    /**
+     * Returns a string representation of the {@link PersistableBundle} that may be suitable for
+     * debugging. It won't print the internal map if its content hasn't been unparcelled.
+     */
+    @Override
+    public synchronized String toString() {
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                return "PersistableBundle[EMPTY_PARCEL]";
+            } else {
+                return "PersistableBundle[mParcelledData.dataSize=" +
+                        mParcelledData.dataSize() + "]";
+            }
+        }
+        return "PersistableBundle[" + mMap.toString() + "]";
+    }
+
+    /** @hide */
+    synchronized public String toShortString() {
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                return "EMPTY_PARCEL";
+            } else {
+                return "mParcelledData.dataSize=" + mParcelledData.dataSize();
+            }
+        }
+        return mMap.toString();
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                proto.write(PersistableBundleProto.PARCELLED_DATA_SIZE, 0);
+            } else {
+                proto.write(PersistableBundleProto.PARCELLED_DATA_SIZE, mParcelledData.dataSize());
+            }
+        } else {
+            proto.write(PersistableBundleProto.MAP_DATA, mMap.toString());
+        }
+
+        proto.end(token);
+    }
+
+    /**
+     * Writes the content of the {@link PersistableBundle} to a {@link OutputStream}.
+     *
+     * <p>The content can be read by a {@link #readFromStream}.
+     *
+     * @see #readFromStream
+     */
+    public void writeToStream(@NonNull OutputStream outputStream) throws IOException {
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
+        serializer.setOutput(outputStream, UTF_8.name());
+        serializer.startTag(null, "bundle");
+        try {
+            saveToXml(serializer);
+        } catch (XmlPullParserException e) {
+            throw new IOException(e);
+        }
+        serializer.endTag(null, "bundle");
+        serializer.flush();
+    }
+
+    /**
+     * Reads a {@link PersistableBundle} from an {@link InputStream}.
+     *
+     * <p>The stream must be generated by {@link #writeToStream}.
+     *
+     * @see #writeToStream
+     */
+    @NonNull
+    public static PersistableBundle readFromStream(@NonNull InputStream inputStream)
+            throws IOException {
+        try {
+            TypedXmlPullParser parser = Xml.newFastPullParser();
+            parser.setInput(inputStream, UTF_8.name());
+            parser.next();
+            return PersistableBundle.restoreFromXml(parser);
+        } catch (XmlPullParserException e) {
+            throw new IOException(e);
+        }
+    }
+}
diff --git a/android-34/android/os/PooledStringReader.java b/android-34/android/os/PooledStringReader.java
new file mode 100644
index 0000000..6fc71c7
--- /dev/null
+++ b/android-34/android/os/PooledStringReader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+/**
+ * Helper class for reading pooling strings from a Parcel.  It must be used
+ * in conjunction with {@link android.os.PooledStringWriter}.  This really needs
+ * to be pushed in to Parcel itself, but doing that is...  complicated.
+ * @hide
+ */
+public class PooledStringReader {
+    private final Parcel mIn;
+
+    /**
+     * The pool of strings we have collected so far.
+     */
+    private final String[] mPool;
+
+    public PooledStringReader(Parcel in) {
+        mIn = in;
+        final int size = in.readInt();
+        mPool = new String[size];
+    }
+
+    public int getStringCount() {
+        return mPool.length;
+    }
+
+    public String readString() {
+        int idx = mIn.readInt();
+        if (idx >= 0) {
+            return mPool[idx];
+        } else {
+            idx = (-idx) - 1;
+            String str = mIn.readString();
+            mPool[idx] = str;
+            return str;
+        }
+    }
+}
diff --git a/android-34/android/os/PooledStringWriter.java b/android-34/android/os/PooledStringWriter.java
new file mode 100644
index 0000000..ee592d9
--- /dev/null
+++ b/android-34/android/os/PooledStringWriter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+import java.util.HashMap;
+
+/**
+ * Helper class for writing pooled strings into a Parcel.  It must be used
+ * in conjunction with {@link android.os.PooledStringReader}.  This really needs
+ * to be pushed in to Parcel itself, but doing that is...  complicated.
+ * @hide
+ */
+public class PooledStringWriter {
+    private final Parcel mOut;
+
+    /**
+     * Book-keeping for writing pooled string objects, mapping strings we have
+     * written so far to their index in the pool.  We deliberately use HashMap
+     * here since performance is critical, we expect to be doing lots of adds to
+     * it, and it is only a temporary object so its overall memory footprint is
+     * not a signifciant issue.
+     */
+    private final HashMap<String, Integer> mPool;
+
+    /**
+     * Book-keeping for writing pooling string objects, indicating where we
+     * started writing the pool, which is where we need to ultimately store
+     * how many strings are in the pool.
+     */
+    private int mStart;
+
+    /**
+     * Next available index in the pool.
+     */
+    private int mNext;
+
+    public PooledStringWriter(Parcel out) {
+        mOut = out;
+        mPool = new HashMap<>();
+        mStart = out.dataPosition();
+        out.writeInt(0); // reserve space for final pool size.
+    }
+
+    public void writeString(String str) {
+        final Integer cur = mPool.get(str);
+        if (cur != null) {
+            mOut.writeInt(cur);
+        } else {
+            mPool.put(str, mNext);
+            mOut.writeInt(-(mNext+1));
+            mOut.writeString(str);
+            mNext++;
+        }
+    }
+
+    public int getStringCount() {
+        return mPool.size();
+    }
+
+    public void finish() {
+        final int pos = mOut.dataPosition();
+        mOut.setDataPosition(mStart);
+        mOut.writeInt(mNext);
+        mOut.setDataPosition(pos);
+    }
+}
diff --git a/android-34/android/os/PowerComponents.java b/android-34/android/os/PowerComponents.java
new file mode 100644
index 0000000..5dffa0a
--- /dev/null
+++ b/android-34/android/os/PowerComponents.java
@@ -0,0 +1,652 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
+import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
+import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
+import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Contains details of battery attribution data broken down to individual power drain types
+ * such as CPU, RAM, GPU etc.
+ *
+ * @hide
+ */
+class PowerComponents {
+    private final BatteryConsumer.BatteryConsumerData mData;
+
+    PowerComponents(@NonNull Builder builder) {
+        mData = builder.mData;
+    }
+
+    PowerComponents(BatteryConsumer.BatteryConsumerData data) {
+        mData = data;
+    }
+
+    /**
+     * Total power consumed by this consumer, aggregated over the specified dimensions, in mAh.
+     */
+    public double getConsumedPower(@NonNull BatteryConsumer.Dimensions dimensions) {
+        if (dimensions.powerComponent != POWER_COMPONENT_ANY) {
+            return mData.getDouble(mData.getKeyOrThrow(dimensions.powerComponent,
+                    dimensions.processState).mPowerColumnIndex);
+        } else if (dimensions.processState != PROCESS_STATE_ANY) {
+            if (!mData.layout.processStateDataIncluded) {
+                throw new IllegalArgumentException(
+                        "No data included in BatteryUsageStats for " + dimensions);
+            }
+            final BatteryConsumer.Key[] keys =
+                    mData.layout.processStateKeys[dimensions.processState];
+            double totalPowerMah = 0;
+            for (int i = keys.length - 1; i >= 0; i--) {
+                totalPowerMah += mData.getDouble(keys[i].mPowerColumnIndex);
+            }
+            return totalPowerMah;
+        } else {
+            return mData.getDouble(mData.layout.totalConsumedPowerColumnIndex);
+        }
+    }
+
+    /**
+     * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+     *
+     * @param key The key of the power component, obtained by calling {@link BatteryConsumer#getKey}
+     *            or {@link BatteryConsumer#getKeys} method.
+     * @return Amount of consumed power in mAh.
+     */
+    public double getConsumedPower(@NonNull BatteryConsumer.Key key) {
+        return mData.getDouble(key.mPowerColumnIndex);
+    }
+
+    /**
+     * Returns the amount of drain attributed to the specified custom drain type.
+     *
+     * @param componentId The ID of the custom power component.
+     * @return Amount of consumed power in mAh.
+     */
+    public double getConsumedPowerForCustomComponent(int componentId) {
+        final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+        if (index >= 0 && index < mData.layout.customPowerComponentCount) {
+            return mData.getDouble(mData.layout.firstCustomConsumedPowerColumn + index);
+        } else {
+            throw new IllegalArgumentException(
+                    "Unsupported custom power component ID: " + componentId);
+        }
+    }
+
+    public String getCustomPowerComponentName(int componentId) {
+        final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+        if (index >= 0 && index < mData.layout.customPowerComponentCount) {
+            try {
+                return mData.layout.customPowerComponentNames[index];
+            } catch (ArrayIndexOutOfBoundsException e) {
+                throw new IllegalArgumentException(
+                        "Unsupported custom power component ID: " + componentId);
+            }
+        } else {
+            throw new IllegalArgumentException(
+                    "Unsupported custom power component ID: " + componentId);
+        }
+    }
+
+    @BatteryConsumer.PowerModel
+    int getPowerModel(BatteryConsumer.Key key) {
+        if (key.mPowerModelColumnIndex == -1) {
+            throw new IllegalStateException(
+                    "Power model IDs were not requested in the BatteryUsageStatsQuery");
+        }
+        return mData.getInt(key.mPowerModelColumnIndex);
+    }
+
+    /**
+     * Returns the amount of time used by the specified component, e.g. CPU, WiFi etc.
+     *
+     * @param key The key of the power component, obtained by calling {@link BatteryConsumer#getKey}
+     *            or {@link BatteryConsumer#getKeys} method.
+     * @return Amount of time in milliseconds.
+     */
+    public long getUsageDurationMillis(BatteryConsumer.Key key) {
+        return mData.getLong(key.mDurationColumnIndex);
+    }
+
+    /**
+     * Returns the amount of usage time attributed to the specified custom component.
+     *
+     * @param componentId The ID of the custom power component.
+     * @return Amount of time in milliseconds.
+     */
+    public long getUsageDurationForCustomComponentMillis(int componentId) {
+        final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+        if (index >= 0 && index < mData.layout.customPowerComponentCount) {
+            return mData.getLong(mData.layout.firstCustomUsageDurationColumn + index);
+        } else {
+            throw new IllegalArgumentException(
+                    "Unsupported custom power component ID: " + componentId);
+        }
+    }
+
+    public void dump(PrintWriter pw, boolean skipEmptyComponents) {
+        String separator = "";
+        StringBuilder sb = new StringBuilder();
+
+        for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+                componentId++) {
+            for (BatteryConsumer.Key key: mData.getKeys(componentId)) {
+                final double componentPower = getConsumedPower(key);
+                final long durationMs = getUsageDurationMillis(key);
+                if (skipEmptyComponents && componentPower == 0 && durationMs == 0) {
+                    continue;
+                }
+
+                sb.append(separator);
+                separator = " ";
+                sb.append(key.toShortString());
+                sb.append("=");
+                sb.append(BatteryStats.formatCharge(componentPower));
+
+                if (durationMs != 0) {
+                    sb.append(" (");
+                    BatteryStats.formatTimeMsNoSpace(sb, durationMs);
+                    sb.append(")");
+                }
+            }
+        }
+
+        final int customComponentCount = mData.layout.customPowerComponentCount;
+        for (int customComponentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+                customComponentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+                        + customComponentCount;
+                customComponentId++) {
+            final double customComponentPower =
+                    getConsumedPowerForCustomComponent(customComponentId);
+            if (skipEmptyComponents && customComponentPower == 0) {
+                continue;
+            }
+            sb.append(separator);
+            separator = " ";
+            sb.append(getCustomPowerComponentName(customComponentId));
+            sb.append("=");
+            sb.append(BatteryStats.formatCharge(customComponentPower));
+        }
+
+        pw.print(sb);
+    }
+
+    /** Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto. */
+    boolean hasStatsProtoData() {
+        return writeStatsProtoImpl(null);
+    }
+
+    /** Writes all atoms.proto POWER_COMPONENTS for this PowerComponents to the given proto. */
+    void writeStatsProto(@NonNull ProtoOutputStream proto) {
+        writeStatsProtoImpl(proto);
+    }
+
+    /**
+     * Returns whether there are any atoms.proto POWER_COMPONENTS data to write to a proto,
+     * and writes it to the given proto if it is non-null.
+     */
+    private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto) {
+        boolean interestingData = false;
+
+        for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+                componentId++) {
+
+            final BatteryConsumer.Key[] keys = mData.getKeys(componentId);
+            for (BatteryConsumer.Key key : keys) {
+                final long powerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower(key));
+                final long durationMs = getUsageDurationMillis(key);
+
+                if (powerDeciCoulombs == 0 && durationMs == 0) {
+                    // No interesting data. Make sure not to even write the COMPONENT int.
+                    continue;
+                }
+
+                interestingData = true;
+                if (proto == null) {
+                    // We're just asked whether there is data, not to actually write it.
+                    // And there is.
+                    return true;
+                }
+
+                if (key.processState == PROCESS_STATE_ANY) {
+                    writePowerComponentUsage(proto,
+                            BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS,
+                            componentId, powerDeciCoulombs, durationMs);
+                } else {
+                    writePowerUsageSlice(proto, componentId, powerDeciCoulombs, durationMs,
+                            key.processState);
+                }
+            }
+        }
+        for (int idx = 0; idx < mData.layout.customPowerComponentCount; idx++) {
+            final int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + idx;
+            final long powerDeciCoulombs =
+                    convertMahToDeciCoulombs(getConsumedPowerForCustomComponent(componentId));
+            final long durationMs = getUsageDurationForCustomComponentMillis(componentId);
+
+            if (powerDeciCoulombs == 0 && durationMs == 0) {
+                // No interesting data. Make sure not to even write the COMPONENT int.
+                continue;
+            }
+
+            interestingData = true;
+            if (proto == null) {
+                // We're just asked whether there is data, not to actually write it. And there is.
+                return true;
+            }
+
+            writePowerComponentUsage(proto,
+                    BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS,
+                    componentId, powerDeciCoulombs, durationMs);
+        }
+        return interestingData;
+    }
+
+    private void writePowerUsageSlice(ProtoOutputStream proto, int componentId,
+            long powerDeciCoulombs, long durationMs, int processState) {
+        final long slicesToken =
+                proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.SLICES);
+        writePowerComponentUsage(proto,
+                BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+                        .POWER_COMPONENT,
+                componentId, powerDeciCoulombs, durationMs);
+
+        final int procState;
+        switch (processState) {
+            case BatteryConsumer.PROCESS_STATE_FOREGROUND:
+                procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+                        .FOREGROUND;
+                break;
+            case BatteryConsumer.PROCESS_STATE_BACKGROUND:
+                procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+                        .BACKGROUND;
+                break;
+            case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE:
+                procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+                        .FOREGROUND_SERVICE;
+                break;
+            case BatteryConsumer.PROCESS_STATE_CACHED:
+                procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+                        .CACHED;
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown process state: " + processState);
+        }
+
+        proto.write(BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+                .PROCESS_STATE, procState);
+
+        proto.end(slicesToken);
+    }
+
+    private void writePowerComponentUsage(ProtoOutputStream proto, long tag, int componentId,
+            long powerDeciCoulombs, long durationMs) {
+        final long token = proto.start(tag);
+        proto.write(
+                BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
+                        .COMPONENT,
+                componentId);
+        proto.write(
+                BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
+                        .POWER_DECI_COULOMBS,
+                powerDeciCoulombs);
+        proto.write(
+                BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
+                        .DURATION_MILLIS,
+                durationMs);
+        proto.end(token);
+    }
+
+    void writeToXml(TypedXmlSerializer serializer) throws IOException {
+        serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+        for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+                componentId++) {
+            final BatteryConsumer.Key[] keys = mData.getKeys(componentId);
+            for (BatteryConsumer.Key key : keys) {
+                final double powerMah = getConsumedPower(key);
+                final long durationMs = getUsageDurationMillis(key);
+                if (powerMah == 0 && durationMs == 0) {
+                    continue;
+                }
+
+                serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+                serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+                if (key.processState != PROCESS_STATE_UNSPECIFIED) {
+                    serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_PROCESS_STATE,
+                            key.processState);
+                }
+                if (powerMah != 0) {
+                    serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+                }
+                if (durationMs != 0) {
+                    serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+                }
+                if (mData.layout.powerModelsIncluded) {
+                    serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL,
+                            getPowerModel(key));
+                }
+                serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+            }
+        }
+
+        final int customComponentEnd = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+                + mData.layout.customPowerComponentCount;
+        for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+                componentId < customComponentEnd;
+                componentId++) {
+            final double powerMah = getConsumedPowerForCustomComponent(componentId);
+            final long durationMs = getUsageDurationForCustomComponentMillis(componentId);
+            if (powerMah == 0 && durationMs == 0) {
+                continue;
+            }
+
+            serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+            serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+            if (powerMah != 0) {
+                serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+            }
+            if (durationMs != 0) {
+                serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+            }
+            serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+        }
+
+        serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+    }
+
+
+    static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder)
+            throws XmlPullParserException, IOException {
+        int eventType = parser.getEventType();
+        if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+                BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+            throw new XmlPullParserException("Invalid XML parser state");
+        }
+
+        while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+                BatteryUsageStats.XML_TAG_POWER_COMPONENTS))
+                && eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG) {
+                switch (parser.getName()) {
+                    case BatteryUsageStats.XML_TAG_COMPONENT: {
+                        int componentId = -1;
+                        int processState = PROCESS_STATE_UNSPECIFIED;
+                        double powerMah = 0;
+                        long durationMs = 0;
+                        int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
+                        for (int i = 0; i < parser.getAttributeCount(); i++) {
+                            switch (parser.getAttributeName(i)) {
+                                case BatteryUsageStats.XML_ATTR_ID:
+                                    componentId = parser.getAttributeInt(i);
+                                    break;
+                                case BatteryUsageStats.XML_ATTR_PROCESS_STATE:
+                                    processState = parser.getAttributeInt(i);
+                                    break;
+                                case BatteryUsageStats.XML_ATTR_POWER:
+                                    powerMah = parser.getAttributeDouble(i);
+                                    break;
+                                case BatteryUsageStats.XML_ATTR_DURATION:
+                                    durationMs = parser.getAttributeLong(i);
+                                    break;
+                                case BatteryUsageStats.XML_ATTR_MODEL:
+                                    model = parser.getAttributeInt(i);
+                                    break;
+                            }
+                        }
+                        final BatteryConsumer.Key key =
+                                builder.mData.getKey(componentId, processState);
+                        builder.setConsumedPower(key, powerMah, model);
+                        builder.setUsageDurationMillis(key, durationMs);
+                        break;
+                    }
+                    case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: {
+                        int componentId = -1;
+                        double powerMah = 0;
+                        long durationMs = 0;
+                        for (int i = 0; i < parser.getAttributeCount(); i++) {
+                            switch (parser.getAttributeName(i)) {
+                                case BatteryUsageStats.XML_ATTR_ID:
+                                    componentId = parser.getAttributeInt(i);
+                                    break;
+                                case BatteryUsageStats.XML_ATTR_POWER:
+                                    powerMah = parser.getAttributeDouble(i);
+                                    break;
+                                case BatteryUsageStats.XML_ATTR_DURATION:
+                                    durationMs = parser.getAttributeLong(i);
+                                    break;
+                            }
+                        }
+                        builder.setConsumedPowerForCustomComponent(componentId, powerMah);
+                        builder.setUsageDurationForCustomComponentMillis(componentId, durationMs);
+                        break;
+                    }
+                }
+            }
+            eventType = parser.next();
+        }
+    }
+
+    /**
+     * Builder for PowerComponents.
+     */
+    static final class Builder {
+        private static final byte POWER_MODEL_UNINITIALIZED = -1;
+
+        private final BatteryConsumer.BatteryConsumerData mData;
+
+        Builder(BatteryConsumer.BatteryConsumerData data) {
+            mData = data;
+            for (BatteryConsumer.Key[] keys : mData.layout.keys) {
+                for (BatteryConsumer.Key key : keys) {
+                    if (key.mPowerModelColumnIndex != -1) {
+                        mData.putInt(key.mPowerModelColumnIndex, POWER_MODEL_UNINITIALIZED);
+                    }
+                }
+            }
+        }
+
+        @NonNull
+        public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower,
+                int powerModel) {
+            mData.putDouble(key.mPowerColumnIndex, componentPower);
+            if (key.mPowerModelColumnIndex != -1) {
+                mData.putInt(key.mPowerModelColumnIndex, powerModel);
+            }
+            return this;
+        }
+
+        /**
+         * Sets the amount of drain attributed to the specified custom drain type.
+         *
+         * @param componentId    The ID of the custom power component.
+         * @param componentPower Amount of consumed power in mAh.
+         */
+        @NonNull
+        public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
+            final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+            if (index < 0 || index >= mData.layout.customPowerComponentCount) {
+                throw new IllegalArgumentException(
+                        "Unsupported custom power component ID: " + componentId);
+            }
+            mData.putDouble(mData.layout.firstCustomConsumedPowerColumn + index, componentPower);
+            return this;
+        }
+
+        @NonNull
+        public Builder setUsageDurationMillis(BatteryConsumer.Key key,
+                long componentUsageDurationMillis) {
+            mData.putLong(key.mDurationColumnIndex, componentUsageDurationMillis);
+            return this;
+        }
+
+        /**
+         * Sets the amount of time used by the specified custom component.
+         *
+         * @param componentId                  The ID of the custom power component.
+         * @param componentUsageDurationMillis Amount of time in milliseconds.
+         */
+        @NonNull
+        public Builder setUsageDurationForCustomComponentMillis(int componentId,
+                long componentUsageDurationMillis) {
+            final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+            if (index < 0 || index >= mData.layout.customPowerComponentCount) {
+                throw new IllegalArgumentException(
+                        "Unsupported custom power component ID: " + componentId);
+            }
+
+            mData.putLong(mData.layout.firstCustomUsageDurationColumn + index,
+                    componentUsageDurationMillis);
+            return this;
+        }
+
+        public void addPowerAndDuration(PowerComponents.Builder other) {
+            addPowerAndDuration(other.mData);
+        }
+
+        public void addPowerAndDuration(PowerComponents other) {
+            addPowerAndDuration(other.mData);
+        }
+
+        private void addPowerAndDuration(BatteryConsumer.BatteryConsumerData otherData) {
+            if (mData.layout.customPowerComponentCount
+                    != otherData.layout.customPowerComponentCount) {
+                throw new IllegalArgumentException(
+                        "Number of custom power components does not match: "
+                                + otherData.layout.customPowerComponentCount
+                                + ", expected: " + mData.layout.customPowerComponentCount);
+            }
+
+            for (int componentId = BatteryConsumer.POWER_COMPONENT_COUNT - 1; componentId >= 0;
+                    componentId--) {
+                final BatteryConsumer.Key[] keys = mData.layout.keys[componentId];
+                for (BatteryConsumer.Key key: keys) {
+                    BatteryConsumer.Key otherKey = null;
+                    for (BatteryConsumer.Key aKey: otherData.layout.keys[componentId]) {
+                        if (aKey.equals(key)) {
+                            otherKey = aKey;
+                            break;
+                        }
+                    }
+
+                    if (otherKey == null) {
+                        continue;
+                    }
+
+                    mData.putDouble(key.mPowerColumnIndex,
+                            mData.getDouble(key.mPowerColumnIndex)
+                                    + otherData.getDouble(otherKey.mPowerColumnIndex));
+                    mData.putLong(key.mDurationColumnIndex,
+                            mData.getLong(key.mDurationColumnIndex)
+                                    + otherData.getLong(otherKey.mDurationColumnIndex));
+
+                    if (key.mPowerModelColumnIndex == -1) {
+                        continue;
+                    }
+
+                    boolean undefined = false;
+                    if (otherKey.mPowerModelColumnIndex == -1) {
+                        undefined = true;
+                    } else {
+                        final int powerModel = mData.getInt(key.mPowerModelColumnIndex);
+                        int otherPowerModel = otherData.getInt(otherKey.mPowerModelColumnIndex);
+                        if (powerModel == POWER_MODEL_UNINITIALIZED) {
+                            mData.putInt(key.mPowerModelColumnIndex, otherPowerModel);
+                        } else if (powerModel != otherPowerModel
+                                && otherPowerModel != POWER_MODEL_UNINITIALIZED) {
+                            undefined = true;
+                        }
+                    }
+
+                    if (undefined) {
+                        mData.putInt(key.mPowerModelColumnIndex,
+                                BatteryConsumer.POWER_MODEL_UNDEFINED);
+                    }
+                }
+            }
+
+            for (int i = mData.layout.customPowerComponentCount - 1; i >= 0; i--) {
+                final int powerColumnIndex = mData.layout.firstCustomConsumedPowerColumn + i;
+                final int otherPowerColumnIndex =
+                        otherData.layout.firstCustomConsumedPowerColumn + i;
+                mData.putDouble(powerColumnIndex,
+                        mData.getDouble(powerColumnIndex) + otherData.getDouble(
+                                otherPowerColumnIndex));
+
+                final int usageColumnIndex = mData.layout.firstCustomUsageDurationColumn + i;
+                final int otherDurationColumnIndex =
+                        otherData.layout.firstCustomUsageDurationColumn + i;
+                mData.putLong(usageColumnIndex,
+                        mData.getLong(usageColumnIndex) + otherData.getLong(
+                                otherDurationColumnIndex)
+                );
+            }
+        }
+
+        /**
+         * Returns the total power accumulated by this builder so far. It may change
+         * by the time the {@code build()} method is called.
+         */
+        public double getTotalPower() {
+            double totalPowerMah = 0;
+            for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+                    componentId++) {
+                totalPowerMah += mData.getDouble(
+                        mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY).mPowerColumnIndex);
+            }
+            for (int i = 0; i < mData.layout.customPowerComponentCount; i++) {
+                totalPowerMah += mData.getDouble(
+                        mData.layout.firstCustomConsumedPowerColumn + i);
+            }
+            return totalPowerMah;
+        }
+
+        /**
+         * Creates a read-only object out of the Builder values.
+         */
+        @NonNull
+        public PowerComponents build() {
+            mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower());
+
+            for (BatteryConsumer.Key[] keys : mData.layout.keys) {
+                for (BatteryConsumer.Key key : keys) {
+                    if (key.mPowerModelColumnIndex != -1) {
+                        if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) {
+                            mData.putInt(key.mPowerModelColumnIndex,
+                                    BatteryConsumer.POWER_MODEL_UNDEFINED);
+                        }
+                    }
+                }
+            }
+
+            return new PowerComponents(this);
+        }
+    }
+}
diff --git a/android-34/android/os/PowerExemptionManager.java b/android-34/android/os/PowerExemptionManager.java
new file mode 100644
index 0000000..17076bc
--- /dev/null
+++ b/android-34/android/os/PowerExemptionManager.java
@@ -0,0 +1,880 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
+import android.content.Context;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Interface to access and modify the permanent and temporary power save allow list. The two lists
+ * are kept separately. Apps placed on the permanent allow list are only removed via an explicit
+ * {@link #removeFromPermanentAllowList(String)} call. Apps allow-listed by default by the system
+ * cannot be removed. Apps placed on the temporary allow list are removed from that allow list after
+ * a predetermined amount of time.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.POWER_EXEMPTION_SERVICE)
+public class PowerExemptionManager {
+    private final Context mContext;
+    // Proxy to DeviceIdleController for now
+    // TODO: migrate to PowerExemptionController
+    private final IDeviceIdleController mService;
+
+    /**
+     * Indicates that an unforeseen event has occurred and the app should be allow-listed to handle
+     * it.
+     */
+    public static final int EVENT_UNSPECIFIED = 0;
+
+    /**
+     * Indicates that an SMS event has occurred and the app should be allow-listed to handle it.
+     */
+    public static final int EVENT_SMS = 1;
+
+    /**
+     * Indicates that an MMS event has occurred and the app should be allow-listed to handle it.
+     */
+    public static final int EVENT_MMS = 2;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EVENT_"}, value = {
+            EVENT_UNSPECIFIED,
+            EVENT_SMS,
+            EVENT_MMS,
+    })
+    public @interface AllowListEvent {
+    }
+
+    /**
+     * Does not place the app on any temporary allow list. Nullifies the previous call to
+     * {@link android.app.BroadcastOptions#setTemporaryAppAllowlist(long, int, int, String)}.
+     * Note: this will not remove the receiver app from the temp allow list.
+     */
+    public static final int TEMPORARY_ALLOW_LIST_TYPE_NONE = -1;
+    /**
+     * Allow the temp allow list behavior, plus allow foreground service start from background.
+     */
+    public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+    /**
+     * Only allow the temp allow list behavior, not allow foreground service start from background.
+     */
+    public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+
+    /**
+     * Delay freezing the app when the broadcast is delivered. This flag is not required if
+     * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED or
+     * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED are specified, as those will
+     * already defer freezing during the allowlist duration.
+     * @hide temporarily until the next release
+     */
+    public static final int TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED = 1 << 2;
+
+    /**
+     * The list of temp allow list types.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_LIST_TYPE_" }, value = {
+            TEMPORARY_ALLOW_LIST_TYPE_NONE,
+            TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+            TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+            TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TempAllowListType {}
+
+    /* Reason codes for BG-FGS-launch. */
+    /**
+     * BG-FGS-launch is denied.
+     * @hide
+     */
+    public static final int REASON_DENIED = -1;
+
+    /* Reason code range 0-9 are reserved for default reasons */
+    /**
+     * The default reason code if reason is unknown.
+     */
+    public static final int REASON_UNKNOWN = 0;
+    /**
+     * Use REASON_OTHER if there is no better choice.
+     */
+    public static final int REASON_OTHER = 1;
+
+    /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT = 10;
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+    /** @hide */
+    public static final int REASON_PROC_STATE_TOP = 12;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BTOP = 13;
+    /** @hide */
+    public static final int REASON_PROC_STATE_FGS = 14;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BFGS = 15;
+
+    /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
+    /** @hide */
+    public static final int REASON_UID_VISIBLE = 50;
+    /** @hide */
+    public static final int REASON_SYSTEM_UID = 51;
+    /** @hide */
+    public static final int REASON_ACTIVITY_STARTER = 52;
+    /** @hide */
+    public static final int REASON_START_ACTIVITY_FLAG = 53;
+    /** @hide */
+    public static final int REASON_FGS_BINDING = 54;
+    /** @hide */
+    public static final int REASON_DEVICE_OWNER = 55;
+    /** @hide */
+    public static final int REASON_PROFILE_OWNER = 56;
+    /** @hide */
+    public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+    /**
+     * START_ACTIVITIES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+    /**
+     * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+    /** @hide */
+    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+    /** @hide */
+    public static final int REASON_DEVICE_DEMO_MODE = 63;
+    /** @hide */
+    public static final int REASON_ALLOWLISTED_PACKAGE = 65;
+    /** @hide */
+    public static final int REASON_APPOP = 66;
+    /** @hide */
+    public static final int REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD = 67;
+    /** @hide */
+    public static final int REASON_OP_ACTIVATE_VPN = 68;
+    /** @hide */
+    public static final int REASON_OP_ACTIVATE_PLATFORM_VPN = 69;
+    /**
+     * Temporarily allowed to have FGS while-in-use permissions.
+     * @hide
+     */
+    public static final int REASON_TEMP_ALLOWED_WHILE_IN_USE = 70;
+    /** @hide */
+    public static final int REASON_CURRENT_INPUT_METHOD = 71;
+
+    /* BG-FGS-launch is allowed by temp-allow-list or system-allow-list.
+       Reason code for temp and system allow list starts here.
+       Reason code range 100-199 are reserved for public reasons. */
+    /**
+     * Set temp-allow-list for location geofence purpose.
+     */
+    public static final int REASON_GEOFENCING = 100;
+    /**
+     * Set temp-allow-list for server push messaging.
+     */
+    public static final int REASON_PUSH_MESSAGING = 101;
+    /**
+     * Set temp-allow-list for server push messaging over the quota.
+     */
+    public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+    /**
+     * Set temp-allow-list for activity recognition.
+     */
+    public static final int REASON_ACTIVITY_RECOGNITION = 103;
+    /**
+     * Set temp-allow-list for transferring accounts between users.
+     */
+    public static final int REASON_ACCOUNT_TRANSFER = 104;
+    /**
+     * Set temp-allow-list for server push messaging that can be deferred.
+     * @hide temporarily until the next release
+     */
+    public static final int REASON_PUSH_MESSAGING_DEFERRABLE = 105;
+
+    /* Reason code range 200-299 are reserved for broadcast actions */
+    /**
+     * Broadcast ACTION_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_BOOT_COMPLETED = 200;
+    /**
+     * Broadcast ACTION_PRE_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_PRE_BOOT_COMPLETED = 201;
+    /**
+     * Broadcast ACTION_LOCKED_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+    /**
+     * All Bluetooth broadcasts.
+     */
+    public static final int REASON_BLUETOOTH_BROADCAST = 203;
+    /**
+     * Broadcast {@link android.content.Intent#ACTION_TIMEZONE_CHANGED}
+     * @hide
+     */
+    public static final int REASON_TIMEZONE_CHANGED = 204;
+    /**
+     * Broadcast {@link android.content.Intent#ACTION_TIME_CHANGED}
+     * @hide
+     */
+    public static final int REASON_TIME_CHANGED = 205;
+    /**
+     * Broadcast {@link android.content.Intent#ACTION_LOCALE_CHANGED}
+     * @hide
+     */
+    public static final int REASON_LOCALE_CHANGED = 206;
+    /**
+     * Broadcast
+     * {@link android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED}
+     * @hide
+     */
+    public static final int REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = 207;
+    /**
+     * Broadcast {@link android.safetycenter.SafetyCenterManager#ACTION_REFRESH_SAFETY_SOURCES}.
+     */
+    public static final int REASON_REFRESH_SAFETY_SOURCES = 208;
+
+    /* Reason code range 300-399 are reserved for other internal reasons */
+    /**
+     * Device idle system allow list, including EXCEPT-IDLE
+     * @hide
+     */
+    public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
+    /** @hide */
+    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
+    /**
+     * AlarmManagerService.
+     * @hide
+     */
+    public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
+    /**
+     * ActiveServices.
+     * @hide
+     */
+    public static final int REASON_SERVICE_LAUNCH = 303;
+    /**
+     * KeyChainSystemService.
+     * @hide
+     */
+    public static final int REASON_KEY_CHAIN = 304;
+    /**
+     * PackageManagerService.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_VERIFIER = 305;
+    /**
+     * SyncManager.
+     * @hide
+     */
+    public static final int REASON_SYNC_MANAGER = 306;
+    /**
+     * DomainVerificationProxyV1.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
+    /**
+     * DomainVerificationProxyV2.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
+    /** @hide */
+    public static final int REASON_VPN = 309;
+    /**
+     * NotificationManagerService.
+     * @hide
+     */
+    public static final int REASON_NOTIFICATION_SERVICE = 310;
+    /**
+     * Broadcast ACTION_MY_PACKAGE_REPLACED.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_REPLACED = 311;
+    /**
+     * LocationProvider.
+     * @hide
+     */
+    @SystemApi
+    public static final int REASON_LOCATION_PROVIDER = 312;
+    /**
+     * MediaButtonReceiver.
+     * @hide
+     */
+    public static final int REASON_MEDIA_BUTTON = 313;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_SMS = 314;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_MMS = 315;
+    /**
+     * Shell app.
+     * @hide
+     */
+    public static final int REASON_SHELL = 316;
+    /**
+     * Media session callbacks.
+     * @hide
+     */
+    public static final int REASON_MEDIA_SESSION_CALLBACK = 317;
+    /**
+     * Dialer app.
+     * @hide
+     */
+    public static final int REASON_ROLE_DIALER = 318;
+    /**
+     * Emergency app.
+     * @hide
+     */
+    public static final int REASON_ROLE_EMERGENCY = 319;
+    /**
+     * System Module.
+     * @hide
+     */
+    public static final int REASON_SYSTEM_MODULE = 320;
+    /**
+     * Carrier privileged app.
+     * @hide
+     */
+    public static final int REASON_CARRIER_PRIVILEGED_APP = 321;
+    /**
+     * Device/Profile owner protected apps.
+     * @hide
+     */
+    public static final int REASON_DPO_PROTECTED_APP = 322;
+    /**
+     * Apps control is disallowed for the user.
+     * @hide
+     */
+    public static final int REASON_DISALLOW_APPS_CONTROL = 323;
+    /**
+     * Active device admin package.
+     * @hide
+     */
+    public static final int REASON_ACTIVE_DEVICE_ADMIN = 324;
+
+    /**
+     * Media notification re-generate during transferring.
+     * @hide
+     */
+    public static final int REASON_MEDIA_NOTIFICATION_TRANSFER = 325;
+
+    /**
+     * Package installer.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_INSTALLER = 326;
+
+    /**
+     * {@link android.app.AppOpsManager#OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS}
+     * set to MODE_ALLOWED
+     * @hide
+     */
+    public static final int REASON_SYSTEM_EXEMPT_APP_OP = 327;
+
+    /** @hide The app requests out-out. */
+    public static final int REASON_OPT_OUT_REQUESTED = 1000;
+
+    /**
+     * The list of BG-FGS-Launch and temp-allow-list reason code.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "REASON_" }, value = {
+            // BG-FGS-Launch reasons.
+            REASON_DENIED,
+            REASON_UNKNOWN,
+            REASON_OTHER,
+            REASON_PROC_STATE_PERSISTENT,
+            REASON_PROC_STATE_PERSISTENT_UI,
+            REASON_PROC_STATE_TOP,
+            REASON_PROC_STATE_BTOP,
+            REASON_PROC_STATE_FGS,
+            REASON_PROC_STATE_BFGS,
+            REASON_UID_VISIBLE,
+            REASON_SYSTEM_UID,
+            REASON_ACTIVITY_STARTER,
+            REASON_START_ACTIVITY_FLAG,
+            REASON_FGS_BINDING,
+            REASON_DEVICE_OWNER,
+            REASON_PROFILE_OWNER,
+            REASON_COMPANION_DEVICE_MANAGER,
+            REASON_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_BACKGROUND_FGS_PERMISSION,
+            REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_INSTR_BACKGROUND_FGS_PERMISSION,
+            REASON_SYSTEM_ALERT_WINDOW_PERMISSION,
+            REASON_DEVICE_DEMO_MODE,
+            REASON_ALLOWLISTED_PACKAGE,
+            REASON_APPOP,
+            REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD,
+            REASON_OP_ACTIVATE_VPN,
+            REASON_OP_ACTIVATE_PLATFORM_VPN,
+            REASON_CURRENT_INPUT_METHOD,
+            REASON_TEMP_ALLOWED_WHILE_IN_USE,
+            // temp and system allow list reasons.
+            REASON_GEOFENCING,
+            REASON_PUSH_MESSAGING,
+            REASON_PUSH_MESSAGING_OVER_QUOTA,
+            REASON_ACTIVITY_RECOGNITION,
+            REASON_ACCOUNT_TRANSFER,
+            REASON_PUSH_MESSAGING_DEFERRABLE,
+            REASON_BOOT_COMPLETED,
+            REASON_PRE_BOOT_COMPLETED,
+            REASON_LOCKED_BOOT_COMPLETED,
+            REASON_BLUETOOTH_BROADCAST,
+            REASON_TIMEZONE_CHANGED,
+            REASON_TIME_CHANGED,
+            REASON_LOCALE_CHANGED,
+            REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
+            REASON_REFRESH_SAFETY_SOURCES,
+            REASON_SYSTEM_ALLOW_LISTED,
+            REASON_ALARM_MANAGER_ALARM_CLOCK,
+            REASON_ALARM_MANAGER_WHILE_IDLE,
+            REASON_SERVICE_LAUNCH,
+            REASON_KEY_CHAIN,
+            REASON_PACKAGE_VERIFIER,
+            REASON_SYNC_MANAGER,
+            REASON_DOMAIN_VERIFICATION_V1,
+            REASON_DOMAIN_VERIFICATION_V2,
+            REASON_VPN,
+            REASON_NOTIFICATION_SERVICE,
+            REASON_PACKAGE_REPLACED,
+            REASON_LOCATION_PROVIDER,
+            REASON_MEDIA_BUTTON,
+            REASON_EVENT_SMS,
+            REASON_EVENT_MMS,
+            REASON_SHELL,
+            REASON_MEDIA_SESSION_CALLBACK,
+            REASON_ROLE_DIALER,
+            REASON_ROLE_EMERGENCY,
+            REASON_SYSTEM_MODULE,
+            REASON_CARRIER_PRIVILEGED_APP,
+            REASON_OPT_OUT_REQUESTED,
+            REASON_DPO_PROTECTED_APP,
+            REASON_DISALLOW_APPS_CONTROL,
+            REASON_ACTIVE_DEVICE_ADMIN,
+            REASON_MEDIA_NOTIFICATION_TRANSFER,
+            REASON_PACKAGE_INSTALLER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReasonCode {}
+
+    /**
+     * @hide
+     */
+    public PowerExemptionManager(@NonNull Context context) {
+        mContext = context;
+        mService = context.getSystemService(DeviceIdleManager.class).getService();
+    }
+
+    /**
+     * Add the specified package to the permanent power save allow list.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void addToPermanentAllowList(@NonNull String packageName) {
+        addToPermanentAllowList(Collections.singletonList(packageName));
+    }
+
+    /**
+     * Add the specified packages to the permanent power save allow list.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void addToPermanentAllowList(@NonNull List<String> packageNames) {
+        try {
+            mService.addPowerSaveWhitelistApps(packageNames);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get a list of app IDs of app that are allow-listed. This does not include temporarily
+     * allow-listed apps.
+     *
+     * @param includingIdle Set to true if the app should be allow-listed from device idle as well
+     *                      as other power save restrictions
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public int[] getAllowListedAppIds(boolean includingIdle) {
+        try {
+            if (includingIdle) {
+                return mService.getAppIdWhitelist();
+            } else {
+                return mService.getAppIdWhitelistExceptIdle();
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if the app is allow-listed from power save restrictions. This does not include
+     * temporarily allow-listed apps.
+     *
+     * @param includingIdle Set to true if the app should be allow-listed from device
+     *                      idle as well as other power save restrictions
+     * @hide
+     */
+    public boolean isAllowListed(@NonNull String packageName, boolean includingIdle) {
+        try {
+            if (includingIdle) {
+                return mService.isPowerSaveWhitelistApp(packageName);
+            } else {
+                return mService.isPowerSaveWhitelistExceptIdleApp(packageName);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove an app from the permanent power save allow list. Only apps that were added via
+     * {@link #addToPermanentAllowList(String)} or {@link #addToPermanentAllowList(List)} will be
+     * removed. Apps allow-listed by default by the system cannot be removed.
+     *
+     * @param packageName The app to remove from the allow list
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void removeFromPermanentAllowList(@NonNull String packageName) {
+        try {
+            mService.removePowerSaveWhitelistApp(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Add an app to the temporary allow list for a short amount of time.
+     *
+     * @param packageName The package to add to the temp allow list
+     * @param durationMs How long to keep the app on the temp allow list for (in milliseconds)
+     * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason a optional human readable reason string, could be null or empty string.
+     */
+    @UserHandleAware
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public void addToTemporaryAllowList(@NonNull String packageName, @ReasonCode int reasonCode,
+            @Nullable String reason, long durationMs) {
+        try {
+            mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
+                    reasonCode, reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Add an app to the temporary allow list for a short amount of time for a specific reason.
+     * The temporary allow list is kept separately from the permanent allow list and apps are
+     * automatically removed from the temporary allow list after a predetermined amount of time.
+     *
+     * @param packageName The package to add to the temp allow list
+     * @param event       The reason to add the app to the temp allow list
+     * @param reasonCode  one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason      A human-readable reason explaining why the app is temp allow-listed. Only
+     *                    used for logging purposes. Could be null or empty string.
+     * @return The duration (in milliseconds) that the app is allow-listed for
+     */
+    @UserHandleAware
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public long addToTemporaryAllowListForEvent(@NonNull String packageName,
+            @ReasonCode int reasonCode, @Nullable String reason, @AllowListEvent int event) {
+        try {
+            switch (event) {
+                case EVENT_MMS:
+                    return mService.addPowerSaveTempWhitelistAppForMms(
+                            packageName, mContext.getUserId(), reasonCode, reason);
+                case EVENT_SMS:
+                    return mService.addPowerSaveTempWhitelistAppForSms(
+                            packageName, mContext.getUserId(), reasonCode, reason);
+                case EVENT_UNSPECIFIED:
+                default:
+                    return mService.whitelistAppTemporarily(
+                            packageName, mContext.getUserId(), reasonCode, reason);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static @ReasonCode int getReasonCodeFromProcState(int procState) {
+        if (procState <= PROCESS_STATE_PERSISTENT) {
+            return REASON_PROC_STATE_PERSISTENT;
+        } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
+            return REASON_PROC_STATE_PERSISTENT_UI;
+        } else if (procState <= PROCESS_STATE_TOP) {
+            return REASON_PROC_STATE_TOP;
+        } else if (procState <= PROCESS_STATE_BOUND_TOP) {
+            return REASON_PROC_STATE_BTOP;
+        } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
+            return REASON_PROC_STATE_FGS;
+        } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+            return REASON_PROC_STATE_BFGS;
+        } else {
+            return REASON_DENIED;
+        }
+    }
+
+    /**
+     * @hide
+     * @return the reason code mapped to statsd for the AppBackgroundRestrictionsInfo atom.
+     */
+    public static int getExemptionReasonForStatsd(@ReasonCode int reasonCode) {
+        switch (reasonCode) {
+            case REASON_SYSTEM_UID:
+                return AppBackgroundRestrictionsInfo.REASON_SYSTEM_UID;
+            case REASON_ALLOWLISTED_PACKAGE:
+                return AppBackgroundRestrictionsInfo.REASON_ALLOWLISTED_PACKAGE;
+            case REASON_COMPANION_DEVICE_MANAGER:
+                return AppBackgroundRestrictionsInfo.REASON_COMPANION_DEVICE_MANAGER;
+            case REASON_DEVICE_DEMO_MODE:
+                return AppBackgroundRestrictionsInfo.REASON_DEVICE_DEMO_MODE;
+            case REASON_DEVICE_OWNER:
+                return AppBackgroundRestrictionsInfo.REASON_DEVICE_OWNER;
+            case REASON_PROFILE_OWNER:
+                return AppBackgroundRestrictionsInfo.REASON_PROFILE_OWNER;
+            case REASON_PROC_STATE_PERSISTENT:
+                return AppBackgroundRestrictionsInfo.REASON_PROC_STATE_PERSISTENT;
+            case REASON_PROC_STATE_PERSISTENT_UI:
+                return AppBackgroundRestrictionsInfo.REASON_PROC_STATE_PERSISTENT_UI;
+            case REASON_OP_ACTIVATE_VPN:
+                return AppBackgroundRestrictionsInfo.REASON_OP_ACTIVATE_VPN;
+            case REASON_OP_ACTIVATE_PLATFORM_VPN:
+                return AppBackgroundRestrictionsInfo.REASON_OP_ACTIVATE_PLATFORM_VPN;
+            case REASON_SYSTEM_MODULE:
+                return AppBackgroundRestrictionsInfo.REASON_SYSTEM_MODULE;
+            case REASON_CARRIER_PRIVILEGED_APP:
+                return AppBackgroundRestrictionsInfo.REASON_CARRIER_PRIVILEGED_APP;
+            case REASON_SYSTEM_ALLOW_LISTED:
+                return AppBackgroundRestrictionsInfo.REASON_SYSTEM_ALLOW_LISTED;
+            case REASON_ROLE_DIALER:
+                return AppBackgroundRestrictionsInfo.REASON_ROLE_DIALER;
+            case REASON_ROLE_EMERGENCY:
+                return AppBackgroundRestrictionsInfo.REASON_ROLE_EMERGENCY;
+            case REASON_DPO_PROTECTED_APP:
+                return AppBackgroundRestrictionsInfo.REASON_DPO_PROTECTED_APP;
+            case REASON_DISALLOW_APPS_CONTROL:
+                return AppBackgroundRestrictionsInfo.REASON_DISALLOW_APPS_CONTROL;
+            case REASON_ACTIVE_DEVICE_ADMIN:
+                return AppBackgroundRestrictionsInfo.REASON_ACTIVE_DEVICE_ADMIN;
+            default:
+                return AppBackgroundRestrictionsInfo.REASON_DENIED;
+        }
+    }
+
+    /**
+     * Return string name of the integer reason code.
+     * @hide
+     * @param reasonCode
+     * @return string name of the reason code.
+     */
+    public static String reasonCodeToString(@ReasonCode int reasonCode) {
+        switch (reasonCode) {
+            case REASON_DENIED:
+                return "DENIED";
+            case REASON_UNKNOWN:
+                return "UNKNOWN";
+            case REASON_OTHER:
+                return "OTHER";
+            case REASON_PROC_STATE_PERSISTENT:
+                return "PROC_STATE_PERSISTENT";
+            case REASON_PROC_STATE_PERSISTENT_UI:
+                return "PROC_STATE_PERSISTENT_UI";
+            case REASON_PROC_STATE_TOP:
+                return "PROC_STATE_TOP";
+            case REASON_PROC_STATE_BTOP:
+                return "PROC_STATE_BTOP";
+            case REASON_PROC_STATE_FGS:
+                return "PROC_STATE_FGS";
+            case REASON_PROC_STATE_BFGS:
+                return "PROC_STATE_BFGS";
+            case REASON_UID_VISIBLE:
+                return "UID_VISIBLE";
+            case REASON_SYSTEM_UID:
+                return "SYSTEM_UID";
+            case REASON_ACTIVITY_STARTER:
+                return "ACTIVITY_STARTER";
+            case REASON_START_ACTIVITY_FLAG:
+                return "START_ACTIVITY_FLAG";
+            case REASON_FGS_BINDING:
+                return "FGS_BINDING";
+            case REASON_DEVICE_OWNER:
+                return "DEVICE_OWNER";
+            case REASON_PROFILE_OWNER:
+                return "PROFILE_OWNER";
+            case REASON_COMPANION_DEVICE_MANAGER:
+                return "COMPANION_DEVICE_MANAGER";
+            case REASON_BACKGROUND_ACTIVITY_PERMISSION:
+                return "BACKGROUND_ACTIVITY_PERMISSION";
+            case REASON_BACKGROUND_FGS_PERMISSION:
+                return "BACKGROUND_FGS_PERMISSION";
+            case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
+                return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
+            case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
+                return "INSTR_BACKGROUND_FGS_PERMISSION";
+            case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
+                return "SYSTEM_ALERT_WINDOW_PERMISSION";
+            case REASON_DEVICE_DEMO_MODE:
+                return "DEVICE_DEMO_MODE";
+            case REASON_ALLOWLISTED_PACKAGE:
+                return "ALLOWLISTED_PACKAGE";
+            case REASON_APPOP:
+                return "APPOP";
+            case REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD:
+                return "ACTIVITY_VISIBILITY_GRACE_PERIOD";
+            case REASON_OP_ACTIVATE_VPN:
+                return "OP_ACTIVATE_VPN";
+            case REASON_OP_ACTIVATE_PLATFORM_VPN:
+                return "OP_ACTIVATE_PLATFORM_VPN";
+            case REASON_CURRENT_INPUT_METHOD:
+                return "CURRENT_INPUT_METHOD";
+            case REASON_TEMP_ALLOWED_WHILE_IN_USE:
+                return "TEMP_ALLOWED_WHILE_IN_USE";
+            case REASON_GEOFENCING:
+                return "GEOFENCING";
+            case REASON_PUSH_MESSAGING:
+                return "PUSH_MESSAGING";
+            case REASON_PUSH_MESSAGING_OVER_QUOTA:
+                return "PUSH_MESSAGING_OVER_QUOTA";
+            case REASON_ACTIVITY_RECOGNITION:
+                return "ACTIVITY_RECOGNITION";
+            case REASON_ACCOUNT_TRANSFER:
+                return "REASON_ACCOUNT_TRANSFER";
+            case REASON_PUSH_MESSAGING_DEFERRABLE:
+                return "PUSH_MESSAGING_DEFERRABLE";
+            case REASON_BOOT_COMPLETED:
+                return "BOOT_COMPLETED";
+            case REASON_PRE_BOOT_COMPLETED:
+                return "PRE_BOOT_COMPLETED";
+            case REASON_LOCKED_BOOT_COMPLETED:
+                return "LOCKED_BOOT_COMPLETED";
+            case REASON_BLUETOOTH_BROADCAST:
+                return "BLUETOOTH_BROADCAST";
+            case REASON_TIMEZONE_CHANGED:
+                return "TIMEZONE_CHANGED";
+            case REASON_TIME_CHANGED:
+                return "TIME_CHANGED";
+            case REASON_LOCALE_CHANGED:
+                return "LOCALE_CHANGED";
+            case REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
+                return "REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
+            case REASON_REFRESH_SAFETY_SOURCES:
+                return "REASON_REFRESH_SAFETY_SOURCES";
+            case REASON_SYSTEM_ALLOW_LISTED:
+                return "SYSTEM_ALLOW_LISTED";
+            case REASON_ALARM_MANAGER_ALARM_CLOCK:
+                return "ALARM_MANAGER_ALARM_CLOCK";
+            case REASON_ALARM_MANAGER_WHILE_IDLE:
+                return "ALARM_MANAGER_WHILE_IDLE";
+            case REASON_SERVICE_LAUNCH:
+                return "SERVICE_LAUNCH";
+            case REASON_KEY_CHAIN:
+                return "KEY_CHAIN";
+            case REASON_PACKAGE_VERIFIER:
+                return "PACKAGE_VERIFIER";
+            case REASON_SYNC_MANAGER:
+                return "SYNC_MANAGER";
+            case REASON_DOMAIN_VERIFICATION_V1:
+                return "DOMAIN_VERIFICATION_V1";
+            case REASON_DOMAIN_VERIFICATION_V2:
+                return "DOMAIN_VERIFICATION_V2";
+            case REASON_VPN:
+                return "VPN";
+            case REASON_NOTIFICATION_SERVICE:
+                return "NOTIFICATION_SERVICE";
+            case REASON_PACKAGE_REPLACED:
+                return "PACKAGE_REPLACED";
+            case REASON_LOCATION_PROVIDER:
+                return "LOCATION_PROVIDER";
+            case REASON_MEDIA_BUTTON:
+                return "MEDIA_BUTTON";
+            case REASON_EVENT_SMS:
+                return "EVENT_SMS";
+            case REASON_EVENT_MMS:
+                return "EVENT_MMS";
+            case REASON_SHELL:
+                return "SHELL";
+            case REASON_MEDIA_SESSION_CALLBACK:
+                return "MEDIA_SESSION_CALLBACK";
+            case REASON_ROLE_DIALER:
+                return "ROLE_DIALER";
+            case REASON_ROLE_EMERGENCY:
+                return "ROLE_EMERGENCY";
+            case REASON_SYSTEM_MODULE:
+                return "SYSTEM_MODULE";
+            case REASON_CARRIER_PRIVILEGED_APP:
+                return "CARRIER_PRIVILEGED_APP";
+            case REASON_DPO_PROTECTED_APP:
+                return "DPO_PROTECTED_APP";
+            case REASON_DISALLOW_APPS_CONTROL:
+                return "DISALLOW_APPS_CONTROL";
+            case REASON_ACTIVE_DEVICE_ADMIN:
+                return "ACTIVE_DEVICE_ADMIN";
+            case REASON_OPT_OUT_REQUESTED:
+                return "REASON_OPT_OUT_REQUESTED";
+            case REASON_MEDIA_NOTIFICATION_TRANSFER:
+                return "REASON_MEDIA_NOTIFICATION_TRANSFER";
+            case REASON_PACKAGE_INSTALLER:
+                return "REASON_PACKAGE_INSTALLER";
+            default:
+                return "(unknown:" + reasonCode + ")";
+        }
+    }
+}
diff --git a/android-34/android/os/PowerManager.java b/android-34/android/os/PowerManager.java
new file mode 100644
index 0000000..d1063f6
--- /dev/null
+++ b/android-34/android/os/PowerManager.java
@@ -0,0 +1,3990 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import android.Manifest.permission;
+import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.app.PropertyInvalidatedCache;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.service.dreams.Sandman;
+import android.sysprop.InitProperties;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * This class lets you query and request control of aspects of the device's power state.
+ */
+@SystemService(Context.POWER_SERVICE)
+public final class PowerManager {
+    private static final String TAG = "PowerManager";
+
+    /* NOTE: Wake lock levels were previously defined as a bit field, except that only a few
+     * combinations were actually supported so the bit field was removed.  This explains
+     * why the numbering scheme is so odd.  If adding a new wake lock level, any unused
+     * value (in frameworks/proto_logging/stats/enums/os/enums.proto) can be used.
+     */
+
+    /**
+     * Wake lock level: Ensures that the CPU is running; the screen and keyboard
+     * backlight will be allowed to go off.
+     * <p>
+     * If the user presses the power button, then the screen will be turned off
+     * but the CPU will be kept on until all partial wake locks have been released.
+     * </p>
+     */
+    public static final int PARTIAL_WAKE_LOCK = OsProtoEnums.PARTIAL_WAKE_LOCK; // 0x00000001
+
+    /**
+     * Wake lock level: Ensures that the screen is on (but may be dimmed);
+     * the keyboard backlight will be allowed to go off.
+     * <p>
+     * If the user presses the power button, then the {@link #SCREEN_DIM_WAKE_LOCK} will be
+     * implicitly released by the system, causing both the screen and the CPU to be turned off.
+     * Contrast with {@link #PARTIAL_WAKE_LOCK}.
+     * </p>
+     *
+     * @deprecated Most applications should use
+     * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead
+     * of this type of wake lock, as it will be correctly managed by the platform
+     * as the user moves between applications and doesn't require a special permission.
+     */
+    @Deprecated
+    public static final int SCREEN_DIM_WAKE_LOCK = OsProtoEnums.SCREEN_DIM_WAKE_LOCK; // 0x00000006
+
+    /**
+     * Wake lock level: Ensures that the screen is on at full brightness;
+     * the keyboard backlight will be allowed to go off.
+     * <p>
+     * If the user presses the power button, then the {@link #SCREEN_BRIGHT_WAKE_LOCK} will be
+     * implicitly released by the system, causing both the screen and the CPU to be turned off.
+     * Contrast with {@link #PARTIAL_WAKE_LOCK}.
+     * </p>
+     *
+     * @deprecated Most applications should use
+     * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead
+     * of this type of wake lock, as it will be correctly managed by the platform
+     * as the user moves between applications and doesn't require a special permission.
+     */
+    @Deprecated
+    public static final int SCREEN_BRIGHT_WAKE_LOCK =
+            OsProtoEnums.SCREEN_BRIGHT_WAKE_LOCK; // 0x0000000a
+
+    /**
+     * Wake lock level: Ensures that the screen and keyboard backlight are on at
+     * full brightness.
+     * <p>
+     * If the user presses the power button, then the {@link #FULL_WAKE_LOCK} will be
+     * implicitly released by the system, causing both the screen and the CPU to be turned off.
+     * Contrast with {@link #PARTIAL_WAKE_LOCK}.
+     * </p>
+     *
+     * @deprecated Most applications should use
+     * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead
+     * of this type of wake lock, as it will be correctly managed by the platform
+     * as the user moves between applications and doesn't require a special permission.
+     */
+    @Deprecated
+    public static final int FULL_WAKE_LOCK = OsProtoEnums.FULL_WAKE_LOCK; // 0x0000001a
+
+    /**
+     * Wake lock level: Turns the screen off when the proximity sensor activates.
+     * <p>
+     * If the proximity sensor detects that an object is nearby, the screen turns off
+     * immediately.  Shortly after the object moves away, the screen turns on again.
+     * </p><p>
+     * A proximity wake lock does not prevent the device from falling asleep
+     * unlike {@link #FULL_WAKE_LOCK}, {@link #SCREEN_BRIGHT_WAKE_LOCK} and
+     * {@link #SCREEN_DIM_WAKE_LOCK}.  If there is no user activity and no other
+     * wake locks are held, then the device will fall asleep (and lock) as usual.
+     * However, the device will not fall asleep while the screen has been turned off
+     * by the proximity sensor because it effectively counts as ongoing user activity.
+     * </p><p>
+     * Since not all devices have proximity sensors, use {@link #isWakeLockLevelSupported}
+     * to determine whether this wake lock level is supported.
+     * </p><p>
+     * Cannot be used with {@link #ACQUIRE_CAUSES_WAKEUP}.
+     * </p>
+     */
+    public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK =
+            OsProtoEnums.PROXIMITY_SCREEN_OFF_WAKE_LOCK; // 0x00000020
+
+    /**
+     * Wake lock level: Put the screen in a low power state and allow the CPU to suspend
+     * if no other wake locks are held.
+     * <p>
+     * This is used by the dream manager to implement doze mode.  It currently
+     * has no effect unless the power manager is in the dozing state.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * {@hide}
+     */
+    public static final int DOZE_WAKE_LOCK = OsProtoEnums.DOZE_WAKE_LOCK; // 0x00000040
+
+    /**
+     * Wake lock level: Keep the device awake enough to allow drawing to occur.
+     * <p>
+     * This is used by the window manager to allow applications to draw while the
+     * system is dozing.  It currently has no effect unless the power manager is in
+     * the dozing state.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * {@hide}
+     */
+    public static final int DRAW_WAKE_LOCK = OsProtoEnums.DRAW_WAKE_LOCK; // 0x00000080
+
+    /**
+     * Mask for the wake lock level component of a combined wake lock level and flags integer.
+     *
+     * @hide
+     */
+    public static final int WAKE_LOCK_LEVEL_MASK = 0x0000ffff;
+
+    /**
+     * Wake lock flag: Turn the screen on when the wake lock is acquired.
+     * <p>
+     * This flag requires {@link android.Manifest.permission#TURN_SCREEN_ON} for apps targeting
+     * Android version {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and higher.
+     * </p><p>
+     * Normally wake locks don't actually wake the device, they just cause the screen to remain on
+     * once it's already on. This flag will cause the device to wake up when the wake lock is
+     * acquired.
+     * </p><p>
+     * Android TV playback devices attempt to turn on the HDMI-connected TV via HDMI-CEC on any
+     * wake-up, including wake-ups triggered by wake locks.
+     * </p><p>
+     * Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
+     * </p>
+     *
+     * @deprecated Most applications should use {@link android.R.attr#turnScreenOn} or
+     * {@link android.app.Activity#setTurnScreenOn(boolean)} instead, as this prevents the previous
+     * foreground app from being resumed first when the screen turns on.
+     */
+    @Deprecated
+    @RequiresPermission(value = android.Manifest.permission.TURN_SCREEN_ON, conditional = true)
+    public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
+
+    /**
+     * Wake lock flag: When this wake lock is released, poke the user activity timer
+     * so the screen stays on for a little longer.
+     * <p>
+     * This will not turn the screen on if it is not already on.
+     * </p><p>
+     * Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
+     * </p>
+     */
+    public static final int ON_AFTER_RELEASE = 0x20000000;
+
+    /**
+     * Wake lock flag: This wake lock is not important for logging events.  If a later
+     * wake lock is acquired that is important, it will be considered the one to log.
+     * @hide
+     */
+    public static final int UNIMPORTANT_FOR_LOGGING = 0x40000000;
+
+    /**
+     * Wake lock flag: This wake lock should be held by the system.
+     *
+     * <p>Meant to allow tests to keep the device awake even when power restrictions are active.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public static final int SYSTEM_WAKELOCK = 0x80000000;
+
+    /**
+     * Flag for {@link WakeLock#release WakeLock.release(int)}: Defer releasing a
+     * {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor
+     * indicates that an object is not in close proximity.
+     */
+    public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1 << 0;
+
+    /**
+     * Flag for {@link WakeLock#release(int)} when called due to timeout.
+     * @hide
+     */
+    public static final int RELEASE_FLAG_TIMEOUT = 1 << 16;
+
+    /**
+     * Brightness value for fully on.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int BRIGHTNESS_ON = 255;
+
+    /**
+     * Brightness value for fully off.
+     * @hide
+     */
+    public static final int BRIGHTNESS_OFF = 0;
+
+    /**
+     * Brightness value for default policy handling by the system.
+     * @hide
+     */
+    public static final int BRIGHTNESS_DEFAULT = -1;
+
+    /**
+     * Brightness value for an invalid value having been stored.
+     * @hide
+     */
+    public static final int BRIGHTNESS_INVALID = -1;
+
+    //Brightness values for new float implementation:
+    /**
+     * Brightness value for fully on as float.
+     * @hide
+     */
+    public static final float BRIGHTNESS_MAX = 1.0f;
+
+    /**
+     * Brightness value for minimum valid brightness as float.
+     * @hide
+     */
+    public static final float BRIGHTNESS_MIN = 0.0f;
+
+    /**
+     * Brightness value for fully off in float.
+     * @hide
+     */
+    public static final float BRIGHTNESS_OFF_FLOAT = -1.0f;
+
+    /**
+     * Invalid brightness value.
+     * @hide
+     */
+    public static final float BRIGHTNESS_INVALID_FLOAT = Float.NaN;
+
+    // Note: Be sure to update android.os.BatteryStats and PowerManager.h
+    // if adding or modifying user activity event constants.
+
+    /**
+     * User activity event type: Unspecified event type.
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_ACTIVITY_EVENT_OTHER = 0;
+
+    /**
+     * User activity event type: Button or key pressed or released.
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_ACTIVITY_EVENT_BUTTON = 1;
+
+    /**
+     * User activity event type: Touch down, move or up.
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_ACTIVITY_EVENT_TOUCH = 2;
+
+    /**
+     * User activity event type: Accessibility taking action on behalf of user.
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;
+
+    /**
+     * User activity event type: {@link android.service.attention.AttentionService} taking action
+     * on behalf of user.
+     * @hide
+     */
+    public static final int USER_ACTIVITY_EVENT_ATTENTION = 4;
+
+    /**
+     * User activity event type: {@link com.android.server.power.FaceDownDetector} taking action
+     * on behalf of user.
+     * @hide
+     */
+    public static final int USER_ACTIVITY_EVENT_FACE_DOWN = 5;
+
+    /**
+     * User activity event type: There is a change in the device state.
+     * @hide
+     */
+    public static final int USER_ACTIVITY_EVENT_DEVICE_STATE = 6;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "USER_ACTIVITY_EVENT_" }, value = {
+            USER_ACTIVITY_EVENT_OTHER,
+            USER_ACTIVITY_EVENT_BUTTON,
+            USER_ACTIVITY_EVENT_TOUCH,
+            USER_ACTIVITY_EVENT_ACCESSIBILITY,
+            USER_ACTIVITY_EVENT_ATTENTION,
+            USER_ACTIVITY_EVENT_FACE_DOWN,
+            USER_ACTIVITY_EVENT_DEVICE_STATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UserActivityEvent{}
+
+    /**
+     *
+     * Convert the user activity event to a string for debugging purposes.
+     * @hide
+     */
+    public static String userActivityEventToString(@UserActivityEvent int userActivityEvent) {
+        switch (userActivityEvent) {
+            case USER_ACTIVITY_EVENT_OTHER: return "other";
+            case USER_ACTIVITY_EVENT_BUTTON: return "button";
+            case USER_ACTIVITY_EVENT_TOUCH: return "touch";
+            case USER_ACTIVITY_EVENT_ACCESSIBILITY: return "accessibility";
+            case USER_ACTIVITY_EVENT_ATTENTION: return "attention";
+            case USER_ACTIVITY_EVENT_FACE_DOWN: return "faceDown";
+            case USER_ACTIVITY_EVENT_DEVICE_STATE: return "deviceState";
+            default: return Integer.toString(userActivityEvent);
+        }
+    }
+
+    /**
+     * User activity flag: If already dimmed, extend the dim timeout
+     * but do not brighten.  This flag is useful for keeping the screen on
+     * a little longer without causing a visible change such as when
+     * the power key is pressed.
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1 << 0;
+
+    /**
+     * User activity flag: Note the user activity as usual but do not
+     * reset the user activity timeout.  This flag is useful for applying
+     * user activity power hints when interacting with the device indirectly
+     * on a secondary screen while allowing the primary screen to go to sleep.
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_ACTIVITY_FLAG_INDIRECT = 1 << 1;
+
+    /**
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_MIN = 0;
+
+    /**
+     * Go to sleep reason code: Going to sleep due by application request.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_APPLICATION = GO_TO_SLEEP_REASON_MIN;
+
+    /**
+     * Go to sleep reason code: Going to sleep due by request of the
+     * device administration policy.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_DEVICE_ADMIN = 1;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to a screen timeout.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int GO_TO_SLEEP_REASON_TIMEOUT = 2;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to the lid switch being closed.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_LID_SWITCH = 3;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to the power button being pressed.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_POWER_BUTTON = 4;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to HDMI.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_HDMI = 5;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to the sleep button being pressed.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_SLEEP_BUTTON = 6;
+
+    /**
+     * Go to sleep reason code: Going to sleep by request of an accessibility service
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_ACCESSIBILITY = 7;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to force-suspend.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to user inattentiveness.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_INATTENTIVE = 9;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to quiescent boot.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10;
+
+    /**
+     * Go to sleep reason code: The last powered on display group has been removed.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED = 11;
+
+    /**
+     * Go to sleep reason code: Every display group has been turned off.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF = 12;
+
+    /**
+     * Go to sleep reason code: A foldable device has been folded.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_DEVICE_FOLD = 13;
+
+    /**
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_MAX =  GO_TO_SLEEP_REASON_DEVICE_FOLD;
+
+    /**
+     * @hide
+     */
+    public static String sleepReasonToString(@GoToSleepReason int sleepReason) {
+        switch (sleepReason) {
+            case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
+            case GO_TO_SLEEP_REASON_APPLICATION: return "application";
+            case GO_TO_SLEEP_REASON_DEVICE_ADMIN: return "device_admin";
+            case GO_TO_SLEEP_REASON_DEVICE_FOLD: return "device_folded";
+            case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed";
+            case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off";
+            case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
+            case GO_TO_SLEEP_REASON_HDMI: return "hdmi";
+            case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
+            case GO_TO_SLEEP_REASON_LID_SWITCH: return "lid_switch";
+            case GO_TO_SLEEP_REASON_POWER_BUTTON: return "power_button";
+            case GO_TO_SLEEP_REASON_QUIESCENT: return "quiescent";
+            case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button";
+            case GO_TO_SLEEP_REASON_TIMEOUT: return "timeout";
+            default: return Integer.toString(sleepReason);
+        }
+    }
+
+    /**
+     * Go to sleep flag: Skip dozing state and directly go to full sleep.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0;
+
+    /**
+     * Go to sleep flag: Sleep softly, go to sleep only if there's no wakelock explicitly keeping
+     * the device awake.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_FLAG_SOFT_SLEEP = 1 << 1;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "BRIGHTNESS_CONSTRAINT_TYPE" }, value = {
+            BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM,
+            BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM,
+            BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT,
+            BRIGHTNESS_CONSTRAINT_TYPE_DIM,
+            BRIGHTNESS_CONSTRAINT_TYPE_DOZE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BrightnessConstraint{}
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM = 0;
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM = 1;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT = 2;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DIM = 3;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DOZE = 4;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "WAKE_REASON_" }, value = {
+            WAKE_REASON_UNKNOWN,
+            WAKE_REASON_POWER_BUTTON,
+            WAKE_REASON_APPLICATION,
+            WAKE_REASON_PLUGGED_IN,
+            WAKE_REASON_GESTURE,
+            WAKE_REASON_CAMERA_LAUNCH,
+            WAKE_REASON_WAKE_KEY,
+            WAKE_REASON_WAKE_MOTION,
+            WAKE_REASON_HDMI,
+            WAKE_REASON_DISPLAY_GROUP_ADDED,
+            WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
+            WAKE_REASON_UNFOLD_DEVICE,
+            WAKE_REASON_DREAM_FINISHED,
+            WAKE_REASON_TILT,
+            WAKE_REASON_TAP,
+            WAKE_REASON_LIFT,
+            WAKE_REASON_BIOMETRIC,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WakeReason{}
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "GO_TO_SLEEP_REASON_" }, value = {
+            GO_TO_SLEEP_REASON_ACCESSIBILITY,
+            GO_TO_SLEEP_REASON_APPLICATION,
+            GO_TO_SLEEP_REASON_DEVICE_ADMIN,
+            GO_TO_SLEEP_REASON_DEVICE_FOLD,
+            GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED,
+            GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF,
+            GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+            GO_TO_SLEEP_REASON_HDMI,
+            GO_TO_SLEEP_REASON_INATTENTIVE,
+            GO_TO_SLEEP_REASON_LID_SWITCH,
+            GO_TO_SLEEP_REASON_POWER_BUTTON,
+            GO_TO_SLEEP_REASON_QUIESCENT,
+            GO_TO_SLEEP_REASON_SLEEP_BUTTON,
+            GO_TO_SLEEP_REASON_TIMEOUT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GoToSleepReason{}
+
+    /**
+     * Wake up reason code: Waking for an unknown reason.
+     * @hide
+     */
+    public static final int WAKE_REASON_UNKNOWN = 0;
+
+    /**
+     * Wake up reason code: Waking up due to power button press.
+     * @hide
+     */
+    public static final int WAKE_REASON_POWER_BUTTON = 1;
+
+    /**
+     * Wake up reason code: Waking up because an application requested it.
+     * @hide
+     */
+    public static final int WAKE_REASON_APPLICATION = 2;
+
+    /**
+     * Wake up reason code: Waking up due to being plugged in or docked on a wireless charger.
+     * @hide
+     */
+    public static final int WAKE_REASON_PLUGGED_IN = 3;
+
+    /**
+     * Wake up reason code: Waking up due to a user performed gesture. This includes user
+     * interactions with UI on the screen such as the notification shade. This does not include
+     * {@link WAKE_REASON_TAP} or {@link WAKE_REASON_LIFT}.
+     * @hide
+     */
+    public static final int WAKE_REASON_GESTURE = 4;
+
+    /**
+     * Wake up reason code: Waking up due to the camera being launched.
+     * @hide
+     */
+    public static final int WAKE_REASON_CAMERA_LAUNCH = 5;
+
+    /**
+     * Wake up reason code: Waking up because a wake key other than power was pressed.
+     * @hide
+     */
+    public static final int WAKE_REASON_WAKE_KEY = 6;
+
+    /**
+     * Wake up reason code: Waking up because a wake motion was performed.
+     *
+     * For example, a trackball that was set to wake the device up was spun.
+     * @hide
+     */
+    public static final int WAKE_REASON_WAKE_MOTION = 7;
+
+    /**
+     * Wake up reason code: Waking due to HDMI.
+     * @hide
+     */
+    public static final int WAKE_REASON_HDMI = 8;
+
+    /**
+     * Wake up reason code: Waking due to the lid being opened.
+     * @hide
+     */
+    public static final int WAKE_REASON_LID = 9;
+
+    /**
+     * Wake up reason code: Waking due to display group being added.
+     * @hide
+     */
+    public static final int WAKE_REASON_DISPLAY_GROUP_ADDED = 10;
+
+    /**
+     * Wake up reason code: Waking due to display group being powered on.
+     * @hide
+     */
+    public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11;
+
+    /**
+     * Wake up reason code: Waking the device due to unfolding of a foldable device.
+     * @hide
+     */
+    public static final int WAKE_REASON_UNFOLD_DEVICE = 12;
+
+    /**
+     * Wake up reason code: Waking the device due to the dream finishing.
+     * @hide
+     */
+    public static final int WAKE_REASON_DREAM_FINISHED = 13;
+
+    /**
+     * Wake up reason code: Waking due to tilt.
+     * @hide
+     */
+    public static final int WAKE_REASON_TILT = 14;
+    /**
+     * Wake up reason code: Waking up due to the user single or double tapping on the screen. This
+     * wake reason is used when the user is not tapping on a specific UI element; rather, the device
+     * wakes up due to a generic tap on the screen.
+     * @hide
+     */
+    public static final int WAKE_REASON_TAP = 15;
+
+    /**
+     * Wake up reason code: Waking up due to a user performed lift gesture.
+     * @hide
+     */
+    public static final int WAKE_REASON_LIFT = 16;
+
+    /**
+     * Wake up reason code: Waking up due to a user interacting with a biometric.
+     * @hide
+     */
+    public static final int WAKE_REASON_BIOMETRIC = 17;
+
+    /**
+     * Convert the wake reason to a string for debugging purposes.
+     * @hide
+     */
+    public static String wakeReasonToString(@WakeReason int wakeReason) {
+        switch (wakeReason) {
+            case WAKE_REASON_UNKNOWN: return "WAKE_REASON_UNKNOWN";
+            case WAKE_REASON_POWER_BUTTON: return "WAKE_REASON_POWER_BUTTON";
+            case WAKE_REASON_APPLICATION: return "WAKE_REASON_APPLICATION";
+            case WAKE_REASON_PLUGGED_IN: return "WAKE_REASON_PLUGGED_IN";
+            case WAKE_REASON_GESTURE: return "WAKE_REASON_GESTURE";
+            case WAKE_REASON_CAMERA_LAUNCH: return "WAKE_REASON_CAMERA_LAUNCH";
+            case WAKE_REASON_WAKE_KEY: return "WAKE_REASON_WAKE_KEY";
+            case WAKE_REASON_WAKE_MOTION: return "WAKE_REASON_WAKE_MOTION";
+            case WAKE_REASON_HDMI: return "WAKE_REASON_HDMI";
+            case WAKE_REASON_LID: return "WAKE_REASON_LID";
+            case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED";
+            case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON";
+            case WAKE_REASON_UNFOLD_DEVICE: return "WAKE_REASON_UNFOLD_DEVICE";
+            case WAKE_REASON_DREAM_FINISHED: return "WAKE_REASON_DREAM_FINISHED";
+            case WAKE_REASON_TILT: return "WAKE_REASON_TILT";
+            case WAKE_REASON_TAP: return "WAKE_REASON_TAP";
+            case WAKE_REASON_LIFT: return "WAKE_REASON_LIFT";
+            case WAKE_REASON_BIOMETRIC: return "WAKE_REASON_BIOMETRIC";
+            default: return Integer.toString(wakeReason);
+        }
+    }
+
+    /**
+     * Information related to the device waking up, triggered by {@link #wakeUp}.
+     *
+     * @hide
+     */
+    public static class WakeData {
+        public WakeData(long wakeTime, @WakeReason int wakeReason, long sleepDurationRealtime) {
+            this.wakeTime = wakeTime;
+            this.wakeReason = wakeReason;
+            this.sleepDurationRealtime = sleepDurationRealtime;
+        }
+        public final long wakeTime;
+        public final @WakeReason int wakeReason;
+        public final long sleepDurationRealtime;
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (o instanceof WakeData) {
+                final WakeData other = (WakeData) o;
+                return wakeTime == other.wakeTime && wakeReason == other.wakeReason
+                        && sleepDurationRealtime == other.sleepDurationRealtime;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(wakeTime, wakeReason, sleepDurationRealtime);
+        }
+    }
+
+    /**
+     * Information related to the device going to sleep, triggered by {@link #goToSleep}.
+     *
+     * @hide
+     */
+    public static class SleepData {
+        public SleepData(long goToSleepUptimeMillis, @GoToSleepReason int goToSleepReason) {
+            this.goToSleepUptimeMillis = goToSleepUptimeMillis;
+            this.goToSleepReason = goToSleepReason;
+        }
+        public final long goToSleepUptimeMillis;
+        public final @GoToSleepReason int goToSleepReason;
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (o instanceof SleepData) {
+                final SleepData other = (SleepData) o;
+                return goToSleepUptimeMillis == other.goToSleepUptimeMillis
+                        && goToSleepReason == other.goToSleepReason;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(goToSleepUptimeMillis, goToSleepReason);
+        }
+    }
+
+    /**
+     * The value to pass as the 'reason' argument to reboot() to reboot into
+     * recovery mode for tasks other than applying system updates, such as
+     * doing factory resets.
+     * <p>
+     * Requires the {@link android.Manifest.permission#RECOVERY}
+     * permission (in addition to
+     * {@link android.Manifest.permission#REBOOT}).
+     * </p>
+     * @hide
+     */
+    public static final String REBOOT_RECOVERY = "recovery";
+
+    /**
+     * The value to pass as the 'reason' argument to reboot() to reboot into
+     * recovery mode for applying system updates.
+     * <p>
+     * Requires the {@link android.Manifest.permission#RECOVERY}
+     * permission (in addition to
+     * {@link android.Manifest.permission#REBOOT}).
+     * </p>
+     * @hide
+     */
+    public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";
+
+    /**
+     * The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on
+     * the device.
+     * @hide
+     */
+    public static final String REBOOT_REQUESTED_BY_DEVICE_OWNER = "deviceowner";
+
+    /**
+     * The 'reason' value used when rebooting in safe mode
+     * @hide
+     */
+    public static final String REBOOT_SAFE_MODE = "safemode";
+
+    /**
+     * The 'reason' value used for rebooting userspace.
+     * @hide
+     */
+    @SystemApi
+    public static final String REBOOT_USERSPACE = "userspace";
+
+    /**
+     * The 'reason' value used when rebooting the device without turning on the screen.
+     * @hide
+     */
+    public static final String REBOOT_QUIESCENT = "quiescent";
+
+    /**
+     * The value to pass as the 'reason' argument to android_reboot().
+     * @hide
+     */
+    public static final String SHUTDOWN_USER_REQUESTED = "userrequested";
+
+    /**
+     * The value to pass as the 'reason' argument to android_reboot() when battery temperature
+     * is too high.
+     * @hide
+     */
+    public static final String SHUTDOWN_BATTERY_THERMAL_STATE = "thermal,battery";
+
+    /**
+     * The value to pass as the 'reason' argument to android_reboot() when device temperature
+     * is too high.
+     * @hide
+     */
+    public static final String SHUTDOWN_THERMAL_STATE = "thermal";
+
+    /**
+     * The value to pass as the 'reason' argument to android_reboot() when device is running
+     * critically low on battery.
+     * @hide
+     */
+    public static final String SHUTDOWN_LOW_BATTERY = "battery";
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "SHUTDOWN_REASON_" }, value = {
+            SHUTDOWN_REASON_UNKNOWN,
+            SHUTDOWN_REASON_SHUTDOWN,
+            SHUTDOWN_REASON_REBOOT,
+            SHUTDOWN_REASON_USER_REQUESTED,
+            SHUTDOWN_REASON_THERMAL_SHUTDOWN,
+            SHUTDOWN_REASON_LOW_BATTERY,
+            SHUTDOWN_REASON_BATTERY_THERMAL
+    })
+    public @interface ShutdownReason {}
+
+    /**
+     * constant for shutdown reason being unknown.
+     * @hide
+     */
+    public static final int SHUTDOWN_REASON_UNKNOWN = 0;
+
+    /**
+     * constant for shutdown reason being normal shutdown.
+     * @hide
+     */
+    public static final int SHUTDOWN_REASON_SHUTDOWN = 1;
+
+    /**
+     * constant for shutdown reason being reboot.
+     * @hide
+     */
+    public static final int SHUTDOWN_REASON_REBOOT = 2;
+
+    /**
+     * constant for shutdown reason being user requested.
+     * @hide
+     */
+    public static final int SHUTDOWN_REASON_USER_REQUESTED = 3;
+
+    /**
+     * constant for shutdown reason being overheating.
+     * @hide
+     */
+    public static final int SHUTDOWN_REASON_THERMAL_SHUTDOWN = 4;
+
+    /**
+     * constant for shutdown reason being low battery.
+     * @hide
+     */
+    public static final int SHUTDOWN_REASON_LOW_BATTERY = 5;
+
+    /**
+     * constant for shutdown reason being critical battery thermal state.
+     * @hide
+     */
+    public static final int SHUTDOWN_REASON_BATTERY_THERMAL = 6;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ServiceType.LOCATION,
+            ServiceType.VIBRATION,
+            ServiceType.ANIMATION,
+            ServiceType.FULL_BACKUP,
+            ServiceType.KEYVALUE_BACKUP,
+            ServiceType.NETWORK_FIREWALL,
+            ServiceType.SCREEN_BRIGHTNESS,
+            ServiceType.SOUND,
+            ServiceType.BATTERY_STATS,
+            ServiceType.DATA_SAVER,
+            ServiceType.FORCE_ALL_APPS_STANDBY,
+            ServiceType.FORCE_BACKGROUND_CHECK,
+            ServiceType.OPTIONAL_SENSORS,
+            ServiceType.AOD,
+            ServiceType.QUICK_DOZE,
+            ServiceType.NIGHT_MODE,
+    })
+    public @interface ServiceType {
+        int NULL = 0;
+        int LOCATION = 1;
+        int VIBRATION = 2;
+        int ANIMATION = 3;
+        int FULL_BACKUP = 4;
+        int KEYVALUE_BACKUP = 5;
+        int NETWORK_FIREWALL = 6;
+        int SCREEN_BRIGHTNESS = 7;
+        int SOUND = 8;
+        int BATTERY_STATS = 9;
+        int DATA_SAVER = 10;
+        int AOD = 14;
+
+        /**
+         * Whether to enable force-app-standby on all apps or not.
+         */
+        int FORCE_ALL_APPS_STANDBY = 11;
+
+        /**
+         * Whether to enable background check on all apps or not.
+         */
+        int FORCE_BACKGROUND_CHECK = 12;
+
+        /**
+         * Whether to disable non-essential sensors. (e.g. edge sensors.)
+         */
+        int OPTIONAL_SENSORS = 13;
+
+        /**
+         * Whether to go into Deep Doze as soon as the screen turns off or not.
+         */
+        int QUICK_DOZE = 15;
+
+        /**
+         * Whether to enable night mode when battery saver is enabled.
+         */
+        int NIGHT_MODE = 16;
+    }
+
+    /**
+     * Either the location providers shouldn't be affected by battery saver,
+     * or battery saver is off.
+     */
+    public static final int LOCATION_MODE_NO_CHANGE = 0;
+
+    /**
+     * In this mode, the GPS based location provider should be disabled when battery saver is on and
+     * the device is non-interactive.
+     */
+    public static final int LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF = 1;
+
+    /**
+     * All location providers should be disabled when battery saver is on and
+     * the device is non-interactive.
+     */
+    public static final int LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2;
+
+    /**
+     * In this mode, all the location providers will be kept available, but location fixes
+     * should only be provided to foreground apps.
+     */
+    public static final int LOCATION_MODE_FOREGROUND_ONLY = 3;
+
+    /**
+     * In this mode, location will not be turned off, but LocationManager will throttle all
+     * requests to providers when the device is non-interactive.
+     */
+    public static final int LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 4;
+
+    /** @hide */
+    public static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE;
+    /** @hide */
+    public static final int MAX_LOCATION_MODE = LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"LOCATION_MODE_"}, value = {
+            LOCATION_MODE_NO_CHANGE,
+            LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
+            LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF,
+            LOCATION_MODE_FOREGROUND_ONLY,
+            LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+    })
+    public @interface LocationPowerSaveMode {}
+
+    /**
+     * In this mode, all active SoundTrigger recognitions are enabled by the SoundTrigger system
+     * service.
+     * @hide
+     */
+    @SystemApi
+    public static final int SOUND_TRIGGER_MODE_ALL_ENABLED = 0;
+    /**
+     * In this mode, only privileged components of the SoundTrigger system service should be
+     * enabled. This functionality is to be used to limit SoundTrigger recognitions to those only
+     * deemed necessary by the system.
+     * @hide
+     */
+    @SystemApi
+    public static final int SOUND_TRIGGER_MODE_CRITICAL_ONLY = 1;
+    /**
+     * In this mode, all active SoundTrigger recognitions should be disabled by the SoundTrigger
+     * system service.
+     * @hide
+     */
+    @SystemApi
+    public static final int SOUND_TRIGGER_MODE_ALL_DISABLED = 2;
+
+    /** @hide */
+    public static final int MIN_SOUND_TRIGGER_MODE = SOUND_TRIGGER_MODE_ALL_ENABLED;
+    /** @hide */
+    public static final int MAX_SOUND_TRIGGER_MODE = SOUND_TRIGGER_MODE_ALL_DISABLED;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SOUND_TRIGGER_MODE_"}, value = {
+            SOUND_TRIGGER_MODE_ALL_ENABLED,
+            SOUND_TRIGGER_MODE_CRITICAL_ONLY,
+            SOUND_TRIGGER_MODE_ALL_DISABLED,
+    })
+    public @interface SoundTriggerPowerSaveMode {}
+
+    /** @hide */
+    public static String locationPowerSaveModeToString(@LocationPowerSaveMode int mode) {
+        switch (mode) {
+            case LOCATION_MODE_NO_CHANGE:
+                return "NO_CHANGE";
+            case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
+                return "GPS_DISABLED_WHEN_SCREEN_OFF";
+            case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
+                return "ALL_DISABLED_WHEN_SCREEN_OFF";
+            case LOCATION_MODE_FOREGROUND_ONLY:
+                return "FOREGROUND_ONLY";
+            case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
+                return "THROTTLE_REQUESTS_WHEN_SCREEN_OFF";
+            default:
+                return Integer.toString(mode);
+        }
+    }
+
+    private static final String CACHE_KEY_IS_POWER_SAVE_MODE_PROPERTY =
+            "cache_key.is_power_save_mode";
+
+    private static final String CACHE_KEY_IS_INTERACTIVE_PROPERTY = "cache_key.is_interactive";
+
+    private static final int MAX_CACHE_ENTRIES = 1;
+
+    private final PropertyInvalidatedCache<Void, Boolean> mPowerSaveModeCache =
+            new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
+                CACHE_KEY_IS_POWER_SAVE_MODE_PROPERTY) {
+                @Override
+                public Boolean recompute(Void query) {
+                    try {
+                        return mService.isPowerSaveMode();
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            };
+
+    private final PropertyInvalidatedCache<Integer, Boolean> mInteractiveCache =
+            new PropertyInvalidatedCache<Integer, Boolean>(MAX_CACHE_ENTRIES,
+                CACHE_KEY_IS_INTERACTIVE_PROPERTY) {
+                @Override
+                public Boolean recompute(Integer displayId) {
+                    try {
+                        if (displayId == null) {
+                            return mService.isInteractive();
+                        } else {
+                            return mService.isDisplayInteractive(displayId);
+                        }
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            };
+
+    final Context mContext;
+    @UnsupportedAppUsage
+    final IPowerManager mService;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    final Handler mHandler;
+    final IThermalService mThermalService;
+
+    /** We lazily initialize it.*/
+    private PowerExemptionManager mPowerExemptionManager;
+
+    private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
+            mListenerMap = new ArrayMap<>();
+
+    /**
+     * {@hide}
+     */
+    public PowerManager(Context context, IPowerManager service, IThermalService thermalService,
+            Handler handler) {
+        mContext = context;
+        mService = service;
+        mThermalService = thermalService;
+        mHandler = handler;
+    }
+
+    private PowerExemptionManager getPowerExemptionManager() {
+        if (mPowerExemptionManager == null) {
+            // No need for synchronization; getSystemService() will return the same object anyway.
+            mPowerExemptionManager = mContext.getSystemService(PowerExemptionManager.class);
+        }
+        return mPowerExemptionManager;
+    }
+
+    /**
+     * Gets the minimum supported screen brightness setting.
+     * The screen may be allowed to become dimmer than this value but
+     * this is the minimum value that can be set by the user.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getMinimumScreenBrightnessSetting() {
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
+    }
+
+    /**
+     * Gets the maximum supported screen brightness setting.
+     * The screen may be allowed to become dimmer than this value but
+     * this is the maximum value that can be set by the user.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getMaximumScreenBrightnessSetting() {
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_screenBrightnessSettingMaximum);
+    }
+
+    /**
+     * Gets the default screen brightness setting.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getDefaultScreenBrightnessSetting() {
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_screenBrightnessSettingDefault);
+    }
+
+    /**
+     * Gets a float screen brightness setting.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public float getBrightnessConstraint(int constraint) {
+        try {
+            return mService.getBrightnessConstraint(constraint);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a new wake lock with the specified level and flags.
+     * <p>
+     * The {@code levelAndFlags} parameter specifies a wake lock level and optional flags
+     * combined using the logical OR operator.
+     * </p><p>
+     * The wake lock levels are: {@link #PARTIAL_WAKE_LOCK},
+     * {@link #FULL_WAKE_LOCK}, {@link #SCREEN_DIM_WAKE_LOCK}
+     * and {@link #SCREEN_BRIGHT_WAKE_LOCK}.  Exactly one wake lock level must be
+     * specified as part of the {@code levelAndFlags} parameter.
+     * </p>
+     * <p>
+     * The wake lock flags are: {@link #ACQUIRE_CAUSES_WAKEUP}
+     * and {@link #ON_AFTER_RELEASE}.  Multiple flags can be combined as part of the
+     * {@code levelAndFlags} parameters.
+     * </p><p>
+     * Call {@link WakeLock#acquire() acquire()} on the object to acquire the
+     * wake lock, and {@link WakeLock#release release()} when you are done.
+     * </p><p>
+     * {@samplecode
+     * PowerManager pm = mContext.getSystemService(PowerManager.class);
+     * PowerManager.WakeLock wl = pm.newWakeLock(
+     *                                      PowerManager.SCREEN_DIM_WAKE_LOCK
+     *                                      | PowerManager.ON_AFTER_RELEASE,
+     *                                      TAG);
+     * wl.acquire();
+     * // ... do work...
+     * wl.release();
+     * }
+     * </p><p>
+     * Although a wake lock can be created without special permissions,
+     * the {@link android.Manifest.permission#WAKE_LOCK} permission is
+     * required to actually acquire or release the wake lock that is returned.
+     *
+     * </p><p>
+     * <b>Device battery life will be significantly affected by the use of this API.</b>
+     * Do not acquire {@link WakeLock}s unless you really need them, use the minimum levels
+     * possible, and be sure to release them as soon as possible.
+     * </p><p class="note">
+     * If using this to keep the screen on, you should strongly consider using
+     * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead.
+     * This window flag will be correctly managed by the platform
+     * as the user moves between applications and doesn't require a special permission.
+     * Additionally using the flag will keep only the appropriate screen on in a
+     * multi-display scenario while using a wake lock will keep every screen powered on.
+     * </p>
+     *
+     * <p>
+     * Recommended naming conventions for tags to make debugging easier:
+     * <ul>
+     * <li>use a unique prefix delimited by a colon for your app/library (e.g.
+     * gmail:mytag) to make it easier to understand where the wake locks comes
+     * from. This namespace will also avoid collision for tags inside your app
+     * coming from different libraries which will make debugging easier.
+     * <li>use constants (e.g. do not include timestamps in the tag) to make it
+     * easier for tools to aggregate similar wake locks. When collecting
+     * debugging data, the platform only monitors a finite number of tags,
+     * using constants will help tools to provide better debugging data.
+     * <li>avoid using Class#getName() or similar method since this class name
+     * can be transformed by java optimizer and obfuscator tools.
+     * <li>avoid wrapping the tag or a prefix to avoid collision with wake lock
+     * tags from the platform (e.g. *alarm*).
+     * <li>never include personally identifiable information for privacy
+     * reasons.
+     * </ul>
+     * </p>
+     *
+     * @param levelAndFlags Combination of wake lock level and flag values defining
+     * the requested behavior of the WakeLock.
+     * @param tag Your class name (or other tag) for debugging purposes.
+     *
+     * @see WakeLock#acquire()
+     * @see WakeLock#release()
+     * @see #PARTIAL_WAKE_LOCK
+     * @see #FULL_WAKE_LOCK
+     * @see #SCREEN_DIM_WAKE_LOCK
+     * @see #SCREEN_BRIGHT_WAKE_LOCK
+     * @see #PROXIMITY_SCREEN_OFF_WAKE_LOCK
+     * @see #ACQUIRE_CAUSES_WAKEUP
+     * @see #ON_AFTER_RELEASE
+     */
+    public WakeLock newWakeLock(int levelAndFlags, String tag) {
+        validateWakeLockParameters(levelAndFlags, tag);
+        return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(),
+                Display.INVALID_DISPLAY);
+    }
+
+    /**
+     * Creates a new wake lock with the specified level and flags.
+     * <p>
+     * The wakelock will only apply to the {@link com.android.server.display.DisplayGroup} of the
+     * provided {@code displayId}. If {@code displayId} is {@link Display#INVALID_DISPLAY} then it
+     * will apply to all {@link com.android.server.display.DisplayGroup DisplayGroups}.
+     *
+     * @param levelAndFlags Combination of wake lock level and flag values defining
+     * the requested behavior of the WakeLock.
+     * @param tag Your class name (or other tag) for debugging purposes.
+     * @param displayId The display id to which this wake lock is tied.
+     *
+     * @hide
+     */
+    public WakeLock newWakeLock(int levelAndFlags, String tag, int displayId) {
+        validateWakeLockParameters(levelAndFlags, tag);
+        return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(), displayId);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static void validateWakeLockParameters(int levelAndFlags, String tag) {
+        switch (levelAndFlags & WAKE_LOCK_LEVEL_MASK) {
+            case PARTIAL_WAKE_LOCK:
+            case SCREEN_DIM_WAKE_LOCK:
+            case SCREEN_BRIGHT_WAKE_LOCK:
+            case FULL_WAKE_LOCK:
+            case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+            case DOZE_WAKE_LOCK:
+            case DRAW_WAKE_LOCK:
+                break;
+            default:
+                throw new IllegalArgumentException("Must specify a valid wake lock level.");
+        }
+        if (tag == null) {
+            throw new IllegalArgumentException("The tag must not be null.");
+        }
+    }
+
+    /**
+     * Notifies the power manager that user activity happened.
+     * <p>
+     * Resets the auto-off timer and brightens the screen if the device
+     * is not asleep.  This is what happens normally when a key or the touch
+     * screen is pressed or when some other user activity occurs.
+     * This method does not wake up the device if it has been put to sleep.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param when The time of the user activity, in the {@link SystemClock#uptimeMillis()}
+     * time base.  This timestamp is used to correctly order the user activity request with
+     * other power management functions.  It should be set
+     * to the timestamp of the input event that caused the user activity.
+     * @param noChangeLights If true, does not cause the keyboard backlight to turn on
+     * because of this event.  This is set when the power key is pressed.
+     * We want the device to stay on while the button is down, but we're about
+     * to turn off the screen so we don't want the keyboard backlight to turn on again.
+     * Otherwise the lights flash on and then off and it looks weird.
+     *
+     * @see #wakeUp
+     * @see #goToSleep
+     *
+     * @removed Requires signature or system permission.
+     * @deprecated Use {@link #userActivity(long, int, int)}.
+     */
+    @Deprecated
+    public void userActivity(long when, boolean noChangeLights) {
+        userActivity(when, USER_ACTIVITY_EVENT_OTHER,
+                noChangeLights ? USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS : 0);
+    }
+
+    /**
+     * Notifies the power manager that user activity happened.
+     * <p>
+     * Resets the auto-off timer and brightens the screen if the device
+     * is not asleep.  This is what happens normally when a key or the touch
+     * screen is pressed or when some other user activity occurs.
+     * This method does not wake up the device if it has been put to sleep.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} or
+     * {@link android.Manifest.permission#USER_ACTIVITY} permission.
+     * </p>
+     *
+     * @param when The time of the user activity, in the {@link SystemClock#uptimeMillis()}
+     * time base.  This timestamp is used to correctly order the user activity request with
+     * other power management functions.  It should be set
+     * to the timestamp of the input event that caused the user activity.
+     * @param event The user activity event.
+     * @param flags Optional user activity flags.
+     *
+     * @see #wakeUp
+     * @see #goToSleep
+     *
+     * @hide Requires signature or system permission.
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DEVICE_POWER,
+            android.Manifest.permission.USER_ACTIVITY
+    })
+    public void userActivity(long when, int event, int flags) {
+        try {
+            mService.userActivity(mContext.getDisplayId(), when, event, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
+     * to turn off.
+     *
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
+     * turned on it will be turned off. If all displays are off as a result of this action the
+     * device will be put to sleep. If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP
+     * default display group} is already off then nothing will happen.
+     *
+     * <p>If the device is an Android TV playback device and the current active source on the
+     * HDMI-connected TV, it will attempt to turn off that TV via HDMI-CEC.
+     *
+     * <p>
+     * Overrides all the wake locks that are held.
+     * This is what happens when the power key is pressed to turn off the screen.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to go to sleep was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the go to sleep request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to go to sleep.
+     *
+     * @see #userActivity
+     * @see #wakeUp
+     *
+     * @removed Requires signature permission.
+     */
+    public void goToSleep(long time) {
+        goToSleep(time, GO_TO_SLEEP_REASON_APPLICATION, 0);
+    }
+
+    /**
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
+     * to turn off.
+     *
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
+     * turned on it will be turned off. If all displays are off as a result of this action the
+     * device will be put to sleep. If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP
+     * default display group} is already off then nothing will happen.
+     *
+     * <p>
+     * Overrides all the wake locks that are held.
+     * This is what happens when the power key is pressed to turn off the screen.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to go to sleep was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the go to sleep request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to go to sleep.
+     * @param reason The reason the device is going to sleep.
+     * @param flags Optional flags to apply when going to sleep.
+     *
+     * @see #userActivity
+     * @see #wakeUp
+     *
+     * @hide Requires signature permission.
+     */
+    @UnsupportedAppUsage
+    public void goToSleep(long time, int reason, int flags) {
+        try {
+            mService.goToSleep(time, reason, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Forces the {@link com.android.server.display.DisplayGroup} of the provided {@code displayId}
+     * to turn off.
+     *
+     * <p>If the {@link com.android.server.display.DisplayGroup} of the provided {@code displayId}
+     * is turned on it will be turned off. If all displays are off as a result of this action the
+     * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup} of
+     * the provided {@code displayId} is already off then nothing will happen.
+     *
+     * <p>Overrides all the wake locks that are held.
+     * This is what happens when the power key is pressed to turn off the screen.
+     *
+     * <p>Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     *
+     * @param displayId The display ID to turn off. If {@code displayId} is
+     * {@link Display#INVALID_DISPLAY}, then all displays are turned off.
+     * @param time The time when the request to go to sleep was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly
+     * order the go to sleep request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to go to sleep.
+     * @param reason The reason the device is going to sleep.
+     * @param flags Optional flags to apply when going to sleep.
+     *
+     * @see #userActivity
+     * @see #wakeUp
+     *
+     * @hide Requires signature permission.
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void goToSleep(int displayId, long time, @GoToSleepReason int reason, int flags) {
+        try {
+            mService.goToSleepWithDisplayId(displayId, time, reason, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
+     * to turn on.
+     *
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
+     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
+     * on then nothing will happen.
+     *
+     * <p>
+     * This is what happens when the power key is pressed to turn on the screen.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to wake up was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the wake up request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to wake up.
+     *
+     * @see #userActivity
+     * @see #goToSleep
+     *
+     * @deprecated Use {@link #wakeUp(long, int, String)} instead.
+     * @removed Requires signature permission.
+     */
+    @Deprecated
+    public void wakeUp(long time) {
+        wakeUp(time, WAKE_REASON_UNKNOWN, "wakeUp");
+    }
+
+    /**
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
+     * to turn on.
+     *
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
+     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
+     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
+     * on then nothing will happen.
+     *
+     * <p>
+     * This is what happens when the power key is pressed to turn on the screen.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to wake up was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the wake up request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to wake up.
+     *
+     * @param details A free form string to explain the specific details behind the wake up for
+     *                debugging purposes.
+     *
+     * @see #userActivity
+     * @see #goToSleep
+     *
+     * @deprecated Use {@link #wakeUp(long, int, String)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public void wakeUp(long time, String details) {
+        wakeUp(time, WAKE_REASON_UNKNOWN, details);
+    }
+
+    /**
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
+     *
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY default display} is turned off it will
+     * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
+     * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
+     *
+     * <p>If the device is an Android TV playback device, it will attempt to turn on the
+     * HDMI-connected TV and become the current active source via the HDMI-CEC One Touch Play
+     * feature.
+     *
+     * <p>
+     * This is what happens when the power key is pressed to turn on the screen.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to wake up was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the wake up request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to wake up.
+     *
+     * @param reason The reason for the wake up.
+     *
+     * @param details A free form string to explain the specific details behind the wake up for
+     *                debugging purposes.
+     *
+     * @see #userActivity
+     * @see #goToSleep
+     * @hide
+     */
+    public void wakeUp(long time, @WakeReason int reason, String details) {
+        try {
+            mService.wakeUp(time, reason, details, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Forces the device to start napping.
+     * <p>
+     * If the device is currently awake, starts dreaming, otherwise does nothing.
+     * When the dream ends or if the dream cannot be started, the device will
+     * either wake up or go to sleep depending on whether there has been recent
+     * user activity.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to nap was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the nap request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to nap.
+     *
+     * @see #wakeUp
+     * @see #goToSleep
+     *
+     * @hide Requires signature permission.
+     */
+    public void nap(long time) {
+        try {
+            mService.nap(time);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the device to start dreaming.
+     * <p>
+     * If dream can not be started, for example if another {@link PowerManager} transition is in
+     * progress, does nothing. Unlike {@link #nap(long)}, this does not put device to sleep when
+     * dream ends.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#READ_DREAM_STATE} and
+     * {@link android.Manifest.permission#WRITE_DREAM_STATE} permissions.
+     * </p>
+     *
+     * @param time The time when the request to nap was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp may be used to correctly
+     * order the dream request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to dream.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.READ_DREAM_STATE,
+            android.Manifest.permission.WRITE_DREAM_STATE })
+    public void dream(long time) {
+        Sandman.startDreamByUserRequest(mContext);
+    }
+
+    /**
+     * Boosts the brightness of the screen to maximum for a predetermined
+     * period of time.  This is used to make the screen more readable in bright
+     * daylight for a short duration.
+     * <p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to boost was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the boost request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to boost.
+     *
+     * @hide Requires signature permission.
+     */
+    public void boostScreenBrightness(long time) {
+        try {
+            mService.boostScreenBrightness(time);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+   /**
+     * Returns true if the specified wake lock level is supported.
+     *
+     * @param level The wake lock level to check.
+     * @return True if the specified wake lock level is supported.
+     */
+    public boolean isWakeLockLevelSupported(int level) {
+        try {
+            return mService.isWakeLockLevelSupported(level);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+      * Returns true if the device is in an interactive state.
+      * <p>
+      * For historical reasons, the name of this method refers to the power state of
+      * the screen but it actually describes the overall interactive state of
+      * the device.  This method has been replaced by {@link #isInteractive}.
+      * </p><p>
+      * The value returned by this method only indicates whether the device is
+      * in an interactive state which may have nothing to do with the screen being
+      * on or off.  To determine the actual state of the screen,
+      * use {@link android.view.Display#getState}.
+      * </p>
+      *
+      * @return True if the device is in an interactive state.
+      *
+      * @deprecated Use {@link #isInteractive} instead.
+      */
+    @Deprecated
+    public boolean isScreenOn() {
+        return isInteractive();
+    }
+
+    /**
+     * Returns true if the device is in an interactive state.
+     * <p>
+     * When this method returns true, the device is awake and ready to interact
+     * with the user (although this is not a guarantee that the user is actively
+     * interacting with the device just this moment).  The main screen is usually
+     * turned on while in this state.  Certain features, such as the proximity
+     * sensor, may temporarily turn off the screen while still leaving the device in an
+     * interactive state.  Note in particular that the device is still considered
+     * to be interactive while dreaming (since dreams can be interactive) but not
+     * when it is dozing or asleep.
+     * </p><p>
+     * When this method returns false, the device is dozing or asleep and must
+     * be awoken before it will become ready to interact with the user again.  The
+     * main screen is usually turned off while in this state.  Certain features,
+     * such as "ambient mode" may cause the main screen to remain on (albeit in a
+     * low power state) to display system-provided content while the device dozes.
+     * </p><p>
+     * The system will send a {@link android.content.Intent#ACTION_SCREEN_ON screen on}
+     * or {@link android.content.Intent#ACTION_SCREEN_OFF screen off} broadcast
+     * whenever the interactive state of the device changes.  For historical reasons,
+     * the names of these broadcasts refer to the power state of the screen
+     * but they are actually sent in response to changes in the overall interactive
+     * state of the device, as described by this method.
+     * </p><p>
+     * Services may use the non-interactive state as a hint to conserve power
+     * since the user is not present.
+     * </p>
+     *
+     * @return True if the device is in an interactive state.
+     *
+     * @see android.content.Intent#ACTION_SCREEN_ON
+     * @see android.content.Intent#ACTION_SCREEN_OFF
+     */
+    public boolean isInteractive() {
+        return mInteractiveCache.query(null);
+    }
+
+    /**
+     * Returns the interactive state for a specific display, which may not be the same as the
+     * global wakefulness (which is true when any display is awake).
+     *
+     * @param displayId
+     * @return whether the given display is present and interactive, or false
+     *
+     * @hide
+     */
+    public boolean isInteractive(int displayId) {
+        return mInteractiveCache.query(displayId);
+    }
+
+    /**
+     * Returns {@code true} if this device supports rebooting userspace.
+     *
+     * <p>This method exists solely for the sake of re-using same logic between {@code PowerManager}
+     * and {@code PowerManagerService}.
+     *
+     * @hide
+     */
+    public static boolean isRebootingUserspaceSupportedImpl() {
+        return InitProperties.is_userspace_reboot_supported().orElse(false);
+    }
+
+    /**
+     * Returns {@code true} if this device supports rebooting userspace.
+     */
+    // TODO(b/138605180): add link to documentation once it's ready.
+    public boolean isRebootingUserspaceSupported() {
+        return isRebootingUserspaceSupportedImpl();
+    }
+
+    /**
+     * Reboot the device.  Will not return if the reboot is successful.
+     * <p>
+     * Requires the {@link android.Manifest.permission#REBOOT} permission.
+     * </p>
+     * <p>
+     * If the {@code reason} string contains ",quiescent", then the screen stays off during reboot
+     * and is not turned on again until the user triggers the device to wake up (for example,
+     * by pressing the power key).
+     * This behavior applies to Android TV devices launched on Android 11 (API level 30) or higher.
+     * </p>
+     *
+     * @param reason code to pass to the kernel (e.g., "recovery") to
+     *               request special boot modes, or null.
+     * @throws UnsupportedOperationException if userspace reboot was requested on a device that
+     *                                       doesn't support it.
+     */
+    @RequiresPermission(permission.REBOOT)
+    public void reboot(@Nullable String reason) {
+        if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
+            throw new UnsupportedOperationException(
+                    "Attempted userspace reboot on a device that doesn't support it");
+        }
+        try {
+            mService.reboot(false, reason, true);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Reboot the device. Will not return if the reboot is successful.
+     * <p>
+     * Requires the {@link android.Manifest.permission#REBOOT} permission.
+     * </p>
+     * @hide
+     */
+    @RequiresPermission(permission.REBOOT)
+    public void rebootSafeMode() {
+        try {
+            mService.rebootSafeMode(false, true);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if the platform has auto power save modes (eg. Doze & app standby) enabled.
+     * This doesn't necessarily mean that the individual features are enabled. For example, if this
+     * returns true, Doze might be enabled while app standby buckets remain disabled.
+     * @hide
+     */
+    @TestApi
+    public boolean areAutoPowerSaveModesEnabled() {
+        try {
+            return mService.areAutoPowerSaveModesEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if the device is currently in power save mode.  When in this mode,
+     * applications should reduce their functionality in order to conserve battery as
+     * much as possible.  You can monitor for changes to this state with
+     * {@link #ACTION_POWER_SAVE_MODE_CHANGED}.
+     *
+     * @return Returns true if currently in low power mode, else false.
+     */
+    public boolean isPowerSaveMode() {
+        return mPowerSaveModeCache.query(null);
+    }
+
+    /**
+     * Set the current power save mode.
+     *
+     * @return True if the set was allowed.
+     *
+     * @hide
+     * @see #isPowerSaveMode()
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DEVICE_POWER,
+            android.Manifest.permission.POWER_SAVER
+    })
+    public boolean setPowerSaveModeEnabled(boolean mode) {
+        try {
+            return mService.setPowerSaveModeEnabled(mode);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the current policy for full power save mode.
+     *
+     * @return The {@link BatterySaverPolicyConfig} which is currently set for the full power save
+     *          policy level.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public BatterySaverPolicyConfig getFullPowerSavePolicy() {
+        try {
+            return mService.getFullPowerSavePolicy();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the policy for full power save mode.
+     *
+     * Any settings set by this API will persist for only one session of full battery saver mode.
+     * The settings set by this API are cleared upon exit of full battery saver mode, and the
+     * caller is expected to set the desired values again for the next full battery saver mode
+     * session if desired.
+     *
+     * Use-cases:
+     * 1. Set policy outside of full battery saver mode
+     *     - full policy set -> enter BS -> policy setting applied -> exit BS -> setting cleared
+     * 2. Set policy inside of full battery saver mode
+     *     - enter BS -> full policy set -> policy setting applied -> exit BS -> setting cleared
+     *
+     * This API is intended to be used with {@link #getFullPowerSavePolicy()} API when a client only
+     * wants to modify a specific setting(s) and leave the remaining policy attributes the same.
+     * Example:
+     * BatterySaverPolicyConfig newFullPolicyConfig =
+     *     new BatterySaverPolicyConfig.Builder(powerManager.getFullPowerSavePolicy())
+     *         .setSoundTriggerMode(PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED)
+     *         .build();
+     * powerManager.setFullPowerSavePolicy(newFullPolicyConfig);
+     *
+     * @return true if there was an effectual change. If full battery saver is enabled, then this
+     * will return true.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DEVICE_POWER,
+            android.Manifest.permission.POWER_SAVER
+    })
+    public boolean setFullPowerSavePolicy(@NonNull BatterySaverPolicyConfig config) {
+        try {
+            return mService.setFullPowerSavePolicy(config);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Updates the current state of dynamic power savings and disable threshold. This is
+     * a signal to the system which an app can update to serve as an indicator that
+     * the user will be in a battery critical situation before being able to plug in.
+     * Only apps with the {@link android.Manifest.permission#POWER_SAVER} permission may do this.
+     * This is a device global state, not a per user setting.
+     *
+     * <p>When enabled, the system may enact various measures for reducing power consumption in
+     * order to help ensure that the user will make it to their next charging point. The most
+     * visible of these will be the automatic enabling of battery saver if the user has set
+     * their battery saver mode to "automatic". Note
+     * that this is NOT simply an on/off switch for features, but rather a hint for the
+     * system to consider enacting these power saving features, some of which have additional
+     * logic around when to activate based on this signal.
+     *
+     * <p>The provided threshold is the percentage the system should consider itself safe at given
+     * the current state of the device. The value is an integer representing a battery level.
+     *
+     * <p>The threshold is meant to set an explicit stopping point for dynamic power savings
+     * functionality so that the dynamic power savings itself remains a signal rather than becoming
+     * an on/off switch for a subset of features.
+     * @hide
+     *
+     * @param powerSaveHint A signal indicating to the system if it believes the
+     * dynamic power savings behaviors should be activated.
+     * @param disableThreshold When the suggesting app believes it would be safe to disable dynamic
+     * power savings behaviors.
+     * @return True if the update was allowed and succeeded.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(permission.POWER_SAVER)
+    public boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold) {
+        try {
+            return mService.setDynamicPowerSaveHint(powerSaveHint, disableThreshold);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the policy for adaptive power save.
+     *
+     * @return true if there was an effectual change. If full battery saver is enabled or the
+     * adaptive policy is not enabled, then this will return false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DEVICE_POWER,
+            android.Manifest.permission.POWER_SAVER
+    })
+    public boolean setAdaptivePowerSavePolicy(@NonNull BatterySaverPolicyConfig config) {
+        try {
+            return mService.setAdaptivePowerSavePolicy(config);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Enables or disables adaptive power save.
+     *
+     * @return true if there was an effectual change. If full battery saver is enabled, then this
+     * will return false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DEVICE_POWER,
+            android.Manifest.permission.POWER_SAVER
+    })
+    public boolean setAdaptivePowerSaveEnabled(boolean enabled) {
+        try {
+            return mService.setAdaptivePowerSaveEnabled(enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates automatic battery saver toggling by the system will be based on percentage.
+     *
+     * @see PowerManager#getPowerSaveModeTrigger()
+     *
+     *  @hide
+     */
+    @SystemApi
+    public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0;
+
+    /**
+     * Indicates automatic battery saver toggling by the system will be based on the state
+     * of the dynamic power savings signal.
+     *
+     * @see PowerManager#setDynamicPowerSaveHint(boolean, int)
+     * @see PowerManager#getPowerSaveModeTrigger()
+     *
+     *  @hide
+     */
+    @SystemApi
+    public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+        POWER_SAVE_MODE_TRIGGER_PERCENTAGE,
+        POWER_SAVE_MODE_TRIGGER_DYNAMIC
+
+    })
+    public @interface AutoPowerSaveModeTriggers {}
+
+
+    /**
+     * Returns the current battery saver control mode. Values it may return are defined in
+     * AutoPowerSaveModeTriggers. Note that this is a global device state, not a per user setting.
+     *
+     * <p>Note: Prior to Android version {@link Build.VERSION_CODES#S}, any app calling this method
+     * was required to hold the {@link android.Manifest.permission#POWER_SAVER} permission. Starting
+     * from Android version {@link Build.VERSION_CODES#S}, that permission is no longer required.
+     *
+     * @return The current value power saver mode for the system.
+     *
+     * @see AutoPowerSaveModeTriggers
+     * @see PowerManager#getPowerSaveModeTrigger()
+     * @hide
+     */
+    @AutoPowerSaveModeTriggers
+    @SystemApi
+    public int getPowerSaveModeTrigger() {
+        try {
+            return mService.getPowerSaveModeTrigger();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allows an app to tell the system how long it believes the battery will last and whether
+     * this estimate is customized based on historical device usage or on a generic configuration.
+     * These estimates will be displayed on system UI surfaces in place of the system computed
+     * value.
+     *
+     * Calling this requires either the {@link android.Manifest.permission#DEVICE_POWER} or the
+     * {@link android.Manifest.permission#BATTERY_PREDICTION} permissions.
+     *
+     * @param timeRemaining  The time remaining as a {@link Duration}.
+     * @param isPersonalized true if personalized based on device usage history, false otherwise.
+     * @throws IllegalStateException if the device is powered or currently charging
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.BATTERY_PREDICTION,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void setBatteryDischargePrediction(@NonNull Duration timeRemaining,
+            boolean isPersonalized) {
+        if (timeRemaining == null) {
+            throw new IllegalArgumentException("time remaining must not be null");
+        }
+        try {
+            mService.setBatteryDischargePrediction(new ParcelDuration(timeRemaining),
+                    isPersonalized);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current battery life remaining estimate.
+     *
+     * @return The estimated battery life remaining as a {@link Duration}. Will be {@code null} if
+     * the device is powered, charging, or an error was encountered.
+     */
+    @Nullable
+    public Duration getBatteryDischargePrediction() {
+        try {
+            final ParcelDuration parcelDuration = mService.getBatteryDischargePrediction();
+            if (parcelDuration == null) {
+                return null;
+            }
+            return parcelDuration.getDuration();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the current battery life remaining estimate is personalized based on device
+     * usage history or not. This value does not take a device's powered or charging state into
+     * account.
+     *
+     * @return A boolean indicating if the current discharge estimate is personalized based on
+     * historical device usage or not.
+     */
+    public boolean isBatteryDischargePredictionPersonalized() {
+        try {
+            return mService.isBatteryDischargePredictionPersonalized();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get data about the battery saver mode for a specific service
+     * @param serviceType unique key for the service, one of {@link ServiceType}
+     * @return Battery saver state data.
+     *
+     * @hide
+     * @see com.android.server.power.batterysaver.BatterySaverPolicy
+     * @see PowerSaveState
+     */
+    public PowerSaveState getPowerSaveState(@ServiceType int serviceType) {
+        try {
+            return mService.getPowerSaveState(serviceType);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns how location features should behave when battery saver is on. When battery saver
+     * is off, this will always return {@link #LOCATION_MODE_NO_CHANGE}.
+     *
+     * <p>This API is normally only useful for components that provide location features.
+     *
+     * @see #isPowerSaveMode()
+     * @see #ACTION_POWER_SAVE_MODE_CHANGED
+     */
+    @LocationPowerSaveMode
+    public int getLocationPowerSaveMode() {
+        final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.LOCATION);
+        if (!powerSaveState.batterySaverEnabled) {
+            return LOCATION_MODE_NO_CHANGE;
+        }
+        return powerSaveState.locationMode;
+    }
+
+    /**
+     * Returns how SoundTrigger features should behave when battery saver is on. When battery saver
+     * is off, this will always return {@link #SOUND_TRIGGER_MODE_ALL_ENABLED}.
+     *
+     * <p>This API is normally only useful for components that provide use SoundTrigger features.
+     *
+     * @see #isPowerSaveMode()
+     * @see #ACTION_POWER_SAVE_MODE_CHANGED
+     *
+     * @hide
+     */
+    @SoundTriggerPowerSaveMode
+    public int getSoundTriggerPowerSaveMode() {
+        final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.SOUND);
+        if (!powerSaveState.batterySaverEnabled) {
+            return SOUND_TRIGGER_MODE_ALL_ENABLED;
+        }
+        return powerSaveState.soundTriggerMode;
+    }
+
+    /**
+     * Returns true if the device is currently in idle mode.  This happens when a device
+     * has been sitting unused and unmoving for a sufficiently long period of time, so that
+     * it decides to go into a lower power-use state.  This may involve things like turning
+     * off network access to apps.  You can monitor for changes to this state with
+     * {@link #ACTION_DEVICE_IDLE_MODE_CHANGED}.
+     *
+     * @return Returns true if currently in active device idle mode, else false.  This is
+     * when idle mode restrictions are being actively applied; it will return false if the
+     * device is in a long-term idle mode but currently running a maintenance window where
+     * restrictions have been lifted.
+     */
+    public boolean isDeviceIdleMode() {
+        try {
+            return mService.isDeviceIdleMode();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if the device is currently in light idle mode.  This happens when a device
+     * has had its screen off for a short time, switching it into a batching mode where we
+     * execute jobs, syncs, networking on a batching schedule.  You can monitor for changes to
+     * this state with {@link #ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED}.
+     *
+     * @return Returns true if currently in active device light idle mode, else false.  This is
+     * when light idle mode restrictions are being actively applied; it will return false if the
+     * device is in a long-term idle mode but currently running a maintenance window where
+     * restrictions have been lifted.
+     */
+    public boolean isDeviceLightIdleMode() {
+        try {
+            return mService.isLightDeviceIdleMode();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #isDeviceLightIdleMode()
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.S,
+            publicAlternatives = "Use {@link #isDeviceLightIdleMode()} instead.")
+    public boolean isLightDeviceIdleMode() {
+        return isDeviceLightIdleMode();
+    }
+
+    /**
+     * Returns true if Low Power Standby is supported on this device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public boolean isLowPowerStandbySupported() {
+        try {
+            return mService.isLowPowerStandbySupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if Low Power Standby is enabled.
+     *
+     * <p>When Low Power Standby is enabled, apps (including apps running foreground services) are
+     * subject to additional restrictions while the device is non-interactive, outside of device
+     * idle maintenance windows: Their network access is disabled, and any wakelocks they hold are
+     * ignored.
+     *
+     * <p>When Low Power Standby is enabled or disabled, a Intent with action
+     * {@link #ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED} is broadcast to registered receivers.
+     */
+    public boolean isLowPowerStandbyEnabled() {
+        try {
+            return mService.isLowPowerStandbyEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether Low Power Standby is enabled.
+     * Does nothing if Low Power Standby is not supported.
+     *
+     * @see #isLowPowerStandbySupported()
+     * @see #isLowPowerStandbyEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void setLowPowerStandbyEnabled(boolean enabled) {
+        try {
+            mService.setLowPowerStandbyEnabled(enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether Low Power Standby should be active during doze maintenance mode.
+     * Does nothing if Low Power Standby is not supported.
+     *
+     * @see #isLowPowerStandbySupported()
+     * @see #isLowPowerStandbyEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance) {
+        try {
+            mService.setLowPowerStandbyActiveDuringMaintenance(activeDuringMaintenance);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Force Low Power Standby restrictions to be active.
+     * Does nothing if Low Power Standby is not supported.
+     *
+     * @see #isLowPowerStandbySupported()
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void forceLowPowerStandbyActive(boolean active) {
+        try {
+            mService.forceLowPowerStandbyActive(active);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the current Low Power Standby policy.
+     *
+     * When the policy changes {@link #ACTION_LOW_POWER_STANDBY_POLICY_CHANGED} is broadcast to
+     * registered receivers.
+     *
+     * @param policy The policy to set. If null, resets to the default policy.
+     * @see #getLowPowerStandbyPolicy
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void setLowPowerStandbyPolicy(@Nullable LowPowerStandbyPolicy policy) {
+        try {
+            mService.setLowPowerStandbyPolicy(LowPowerStandbyPolicy.toParcelable(policy));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the current Low Power Standby policy.
+     *
+     * When the policy changes {@link #ACTION_LOW_POWER_STANDBY_POLICY_CHANGED} is broadcast to
+     * registered receivers.
+     *
+     * @see #setLowPowerStandbyPolicy
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public LowPowerStandbyPolicy getLowPowerStandbyPolicy() {
+        try {
+            return LowPowerStandbyPolicy.fromParcelable(mService.getLowPowerStandbyPolicy());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if the calling package is exempt from Low Power Standby restrictions or
+     * Low Power Standby is disabled (so Low Power Standby does not restrict apps),
+     * false otherwise.
+     */
+    public boolean isExemptFromLowPowerStandby() {
+        try {
+            return mService.isExemptFromLowPowerStandby();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if Low Power Standby is disabled (so Low Power Standby does not restrict apps),
+     * or apps may be automatically exempt from Low Power Standby restrictions for the given reason.
+     *
+     * The system may exempt apps from Low Power Standby restrictions when using allowed features.
+     * For example, if {@link #LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION} is allowed,
+     * then apps with active voice interaction sessions are exempt from restrictions.
+     */
+    public boolean isAllowedInLowPowerStandby(@LowPowerStandbyAllowedReason int reason) {
+        try {
+            return mService.isReasonAllowedInLowPowerStandby(reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if Low Power Standby is disabled (so Low Power Standby does not restrict apps),
+     * or apps are allowed to use a given feature during Low Power Standby.
+     */
+    public boolean isAllowedInLowPowerStandby(@NonNull String feature) {
+        try {
+            return mService.isFeatureAllowedInLowPowerStandby(feature);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a new Low Power Standby ports lock.
+     *
+     * <p>A Low Power Standby ports lock requests that the given ports remain open during
+     * Low Power Standby.
+     * Call {@link LowPowerStandbyPortsLock#acquire} to acquire the lock.
+     * This request is only respected if the calling package is exempt
+     * (see {@link #isExemptFromLowPowerStandby()}), and until the returned
+     * {@code LowPowerStandbyPorts} object is destroyed or has
+     * {@link LowPowerStandbyPortsLock#release} called on it.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SET_LOW_POWER_STANDBY_PORTS)
+    @NonNull
+    public LowPowerStandbyPortsLock newLowPowerStandbyPortsLock(
+            @NonNull List<LowPowerStandbyPortDescription> ports) {
+        LowPowerStandbyPortsLock standbyPorts = new LowPowerStandbyPortsLock(ports);
+        return standbyPorts;
+    }
+
+    /**
+     * Gets all ports that should remain open in standby.
+     * Only includes ports requested by exempt packages (see {@link #getLowPowerStandbyPolicy()}).
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    @NonNull
+    public List<LowPowerStandbyPortDescription> getActiveLowPowerStandbyPorts() {
+        try {
+            return LowPowerStandbyPortDescription.fromParcelable(
+                    mService.getActiveLowPowerStandbyPorts());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return whether the given application package name is on the device's power allowlist.
+     * Apps can be placed on the allowlist through the settings UI invoked by
+     * {@link android.provider.Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}.
+     * <p>Being on the power allowlist means that the system will not apply most power saving
+     * features to the app. Guardrails for extreme cases may still be applied.
+     */
+    public boolean isIgnoringBatteryOptimizations(String packageName) {
+        return getPowerExemptionManager().isAllowListed(packageName, true);
+    }
+
+    /**
+     * Turn off the device.
+     *
+     * @param confirm If true, shows a shutdown confirmation dialog.
+     * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
+     * @param wait If true, this call waits for the shutdown to complete and does not return.
+     *
+     * @hide
+     */
+    public void shutdown(boolean confirm, String reason, boolean wait) {
+        try {
+            mService.shutdown(confirm, reason, wait);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This function checks if the device has implemented Sustained Performance
+     * Mode. This needs to be checked only once and is constant for a particular
+     * device/release.
+     *
+     * Sustained Performance Mode is intended to provide a consistent level of
+     * performance for prolonged amount of time.
+     *
+     * Applications should check if the device supports this mode, before using
+     * {@link android.view.Window#setSustainedPerformanceMode}.
+     *
+     * @return Returns True if the device supports it, false otherwise.
+     *
+     * @see android.view.Window#setSustainedPerformanceMode
+     */
+    public boolean isSustainedPerformanceModeSupported() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_sustainedPerformanceModeSupported);
+    }
+
+    /**
+     * Thermal status code: Not under throttling.
+     */
+    public static final int THERMAL_STATUS_NONE = Temperature.THROTTLING_NONE;
+
+    /**
+     * Thermal status code: Light throttling where UX is not impacted.
+     */
+    public static final int THERMAL_STATUS_LIGHT = Temperature.THROTTLING_LIGHT;
+
+    /**
+     * Thermal status code: Moderate throttling where UX is not largely impacted.
+     */
+    public static final int THERMAL_STATUS_MODERATE = Temperature.THROTTLING_MODERATE;
+
+    /**
+     * Thermal status code: Severe throttling where UX is largely impacted.
+     */
+    public static final int THERMAL_STATUS_SEVERE = Temperature.THROTTLING_SEVERE;
+
+    /**
+     * Thermal status code: Platform has done everything to reduce power.
+     */
+    public static final int THERMAL_STATUS_CRITICAL = Temperature.THROTTLING_CRITICAL;
+
+    /**
+     * Thermal status code: Key components in platform are shutting down due to thermal condition.
+     * Device functionalities will be limited.
+     */
+    public static final int THERMAL_STATUS_EMERGENCY = Temperature.THROTTLING_EMERGENCY;
+
+    /**
+     * Thermal status code: Need shutdown immediately.
+     */
+    public static final int THERMAL_STATUS_SHUTDOWN = Temperature.THROTTLING_SHUTDOWN;
+
+    /** @hide */
+    @IntDef(prefix = { "THERMAL_STATUS_" }, value = {
+            THERMAL_STATUS_NONE,
+            THERMAL_STATUS_LIGHT,
+            THERMAL_STATUS_MODERATE,
+            THERMAL_STATUS_SEVERE,
+            THERMAL_STATUS_CRITICAL,
+            THERMAL_STATUS_EMERGENCY,
+            THERMAL_STATUS_SHUTDOWN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ThermalStatus {}
+
+    /**
+     * This function returns the current thermal status of the device.
+     *
+     * @return thermal status as int, {@link #THERMAL_STATUS_NONE} if device in not under
+     * thermal throttling.
+     */
+    public @ThermalStatus int getCurrentThermalStatus() {
+        try {
+            return mThermalService.getCurrentThermalStatus();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Listener passed to
+     * {@link PowerManager#addThermalStatusListener} and
+     * {@link PowerManager#removeThermalStatusListener}
+     * to notify caller of thermal status has changed.
+     */
+    public interface OnThermalStatusChangedListener {
+
+        /**
+         * Called when overall thermal throttling status changed.
+         * @param status defined in {@link android.os.Temperature}.
+         */
+        void onThermalStatusChanged(@ThermalStatus int status);
+    }
+
+
+    /**
+     * This function adds a listener for thermal status change, listen call back will be
+     * enqueued tasks on the main thread
+     *
+     * @param listener listener to be added,
+     */
+    public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
+        Objects.requireNonNull(listener, "listener cannot be null");
+        addThermalStatusListener(mContext.getMainExecutor(), listener);
+    }
+
+    /**
+     * This function adds a listener for thermal status change.
+     *
+     * @param executor {@link Executor} to handle listener callback.
+     * @param listener listener to be added.
+     */
+    public void addThermalStatusListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnThermalStatusChangedListener listener) {
+        Objects.requireNonNull(listener, "listener cannot be null");
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Preconditions.checkArgument(!mListenerMap.containsKey(listener),
+                "Listener already registered: %s", listener);
+        IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
+            @Override
+            public void onStatusChange(int status) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onThermalStatusChanged(status));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        };
+        try {
+            if (mThermalService.registerThermalStatusListener(internalListener)) {
+                mListenerMap.put(listener, internalListener);
+            } else {
+                throw new RuntimeException("Listener failed to set");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This function removes a listener for thermal status change
+     *
+     * @param listener listener to be removed
+     */
+    public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
+        Objects.requireNonNull(listener, "listener cannot be null");
+        IThermalStatusListener internalListener = mListenerMap.get(listener);
+        Preconditions.checkArgument(internalListener != null, "Listener was not added");
+        try {
+            if (mThermalService.unregisterThermalStatusListener(internalListener)) {
+                mListenerMap.remove(listener);
+            } else {
+                throw new RuntimeException("Listener failed to remove");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @CurrentTimeMillisLong
+    private final AtomicLong mLastHeadroomUpdate = new AtomicLong(0L);
+    private static final int MINIMUM_HEADROOM_TIME_MILLIS = 500;
+
+    /**
+     * Provides an estimate of how much thermal headroom the device currently has before hitting
+     * severe throttling.
+     *
+     * Note that this only attempts to track the headroom of slow-moving sensors, such as the skin
+     * temperature sensor. This means that there is no benefit to calling this function more
+     * frequently than about once per second, and attempts to call significantly more frequently may
+     * result in the function returning {@code NaN}.
+     * <p>
+     * In addition, in order to be able to provide an accurate forecast, the system does not attempt
+     * to forecast until it has multiple temperature samples from which to extrapolate. This should
+     * only take a few seconds from the time of the first call, but during this time, no forecasting
+     * will occur, and the current headroom will be returned regardless of the value of
+     * {@code forecastSeconds}.
+     * <p>
+     * The value returned is a non-negative float that represents how much of the thermal envelope
+     * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is (or
+     * will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the CPU,
+     * GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping to specific
+     * thermal status levels beyond that point. This means that values greater than 1.0 may
+     * correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
+     * <p>
+     * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any
+     * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale
+     * linearly with temperature, though temperature changes over time are typically not linear.
+     * Negative values will be clamped to 0.0 before returning.
+     *
+     * @param forecastSeconds how many seconds in the future to forecast. Given that device
+     *                        conditions may change at any time, forecasts from further in the
+     *                        future will likely be less accurate than forecasts in the near future.
+     * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling
+     *         threshold, as described above. Returns NaN if the device does not support this
+     *         functionality or if this function is called significantly faster than once per
+     *         second.
+     */
+    public float getThermalHeadroom(@IntRange(from = 0, to = 60) int forecastSeconds) {
+        // Rate-limit calls into the thermal service
+        long now = SystemClock.elapsedRealtime();
+        long timeSinceLastUpdate = now - mLastHeadroomUpdate.get();
+        if (timeSinceLastUpdate < MINIMUM_HEADROOM_TIME_MILLIS) {
+            return Float.NaN;
+        }
+
+        try {
+            float forecast = mThermalService.getThermalHeadroom(forecastSeconds);
+            mLastHeadroomUpdate.set(SystemClock.elapsedRealtime());
+            return forecast;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * If true, the doze component is not started until after the screen has been
+     * turned off and the screen off animation has been performed.
+     * @hide
+     */
+    public void setDozeAfterScreenOff(boolean dozeAfterScreenOf) {
+        try {
+            mService.setDozeAfterScreenOff(dozeAfterScreenOf);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if ambient display is available on the device.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE)
+    public boolean isAmbientDisplayAvailable() {
+        try {
+            return mService.isAmbientDisplayAvailable();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * If true, suppresses the current ambient display configuration and disables ambient display.
+     *
+     * <p>This method has no effect if {@link #isAmbientDisplayAvailable()} is false.
+     *
+     * @param token A persistable identifier for the ambient display suppression that is unique
+     *              within the calling application.
+     * @param suppress If set to {@code true}, ambient display will be suppressed. If set to
+     *                 {@code false}, ambient display will no longer be suppressed for the given
+     *                 token.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    public void suppressAmbientDisplay(@NonNull String token, boolean suppress) {
+        try {
+            mService.suppressAmbientDisplay(token, suppress);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if ambient display is suppressed by the calling app with the given
+     * {@code token}.
+     *
+     * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false.
+     *
+     * @param token The identifier of the ambient display suppression.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE)
+    public boolean isAmbientDisplaySuppressedForToken(@NonNull String token) {
+        try {
+            return mService.isAmbientDisplaySuppressedForToken(token);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if ambient display is suppressed by <em>any</em> app with <em>any</em> token.
+     *
+     * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE)
+    public boolean isAmbientDisplaySuppressed() {
+        try {
+            return mService.isAmbientDisplaySuppressed();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if ambient display is suppressed by the given {@code appUid} with the given
+     * {@code token}.
+     *
+     * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false.
+     *
+     * @param token The identifier of the ambient display suppression.
+     * @param appUid The uid of the app that suppressed ambient display.
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.READ_DREAM_STATE,
+            android.Manifest.permission.READ_DREAM_SUPPRESSION })
+    public boolean isAmbientDisplaySuppressedForTokenByApp(@NonNull String token, int appUid) {
+        try {
+            return mService.isAmbientDisplaySuppressedForTokenByApp(token, appUid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the reason the phone was last shutdown. Calling app must have the
+     * {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
+     * @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
+     * not be accessed.
+     * @hide
+     */
+    @ShutdownReason
+    public int getLastShutdownReason() {
+        try {
+            return mService.getLastShutdownReason();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the reason the device last went to sleep (i.e. the last value of
+     * the second argument of {@link #goToSleep(long, int, int) goToSleep}).
+     *
+     * @return One of the {@code GO_TO_SLEEP_REASON_*} constants.
+     *
+     * @hide
+     */
+    @GoToSleepReason
+    public int getLastSleepReason() {
+        try {
+            return mService.getLastSleepReason();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Forces the device to go to suspend, even if there are currently wakelocks being held.
+     * <b>Caution</b>
+     * This is a very dangerous command as it puts the device to sleep immediately. Apps and parts
+     * of the system will not be notified and will not have an opportunity to save state prior to
+     * the device going to suspend.
+     * This method should only be used in very rare circumstances where the device is intended
+     * to appear as completely off to the user and they have a well understood, reliable way of
+     * re-enabling it.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @return true on success, false otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public boolean forceSuspend() {
+        try {
+            return mService.forceSuspend();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Intent that is broadcast when the enhanced battery discharge prediction changes. The new
+     * value can be retrieved via {@link #getBatteryDischargePrediction()}.
+     * This broadcast is only sent to registered receivers.
+     *
+     * @hide
+     */
+    @TestApi
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED =
+            "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
+
+    /**
+     * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_POWER_SAVE_MODE_CHANGED
+            = "android.os.action.POWER_SAVE_MODE_CHANGED";
+
+    /**
+     * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
+     * @hide
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL
+            = "android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL";
+
+    /**
+     * Intent that is broadcast when the state of {@link #isDeviceIdleMode()} changes.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_IDLE_MODE_CHANGED
+            = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
+
+    /**
+     * Intent that is broadcast when the state of {@link #isDeviceLightIdleMode()} changes.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SuppressLint("ActionValue") // Need to do "LIGHT_DEVICE_IDLE..." for legacy reasons
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED =
+            // Use the old string so we don't break legacy apps.
+            "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
+
+    /**
+     * @see #ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553,
+            publicAlternatives = "Use {@link #ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED} instead")
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED =
+            ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED;
+
+    /**
+     * @hide Intent that is broadcast when the set of power save allowlist apps has changed.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_POWER_SAVE_WHITELIST_CHANGED
+            = "android.os.action.POWER_SAVE_WHITELIST_CHANGED";
+
+    /**
+     * @hide Intent that is broadcast when the set of temporarily allowlisted apps has changed.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED
+            = "android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED";
+
+    /**
+     * Intent that is broadcast when Low Power Standby is enabled or disabled.
+     * This broadcast is only sent to registered receivers.
+     *
+     * @see #isLowPowerStandbyEnabled()
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED =
+            "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
+
+    /**
+     * Intent that is broadcast when Low Power Standby policy is changed.
+     * This broadcast is only sent to registered receivers.
+     *
+     * @see #isExemptFromLowPowerStandby()
+     * @see #isAllowedInLowPowerStandby(int)
+     * @see #isAllowedInLowPowerStandby(String)
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LOW_POWER_STANDBY_POLICY_CHANGED =
+            "android.os.action.LOW_POWER_STANDBY_POLICY_CHANGED";
+
+    /**
+     * Intent that is broadcast when Low Power Standby exempt ports change.
+     * This broadcast is only sent to registered receivers.
+     *
+     * @see #getActiveLowPowerStandbyPorts
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_LOW_POWER_STANDBY)
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LOW_POWER_STANDBY_PORTS_CHANGED =
+            "android.os.action.LOW_POWER_STANDBY_PORTS_CHANGED";
+
+    /**
+     * Signals that wake-on-lan/wake-on-wlan is allowed in Low Power Standby.
+     *
+     * <p>If Low Power Standby is enabled ({@link #isLowPowerStandbyEnabled()}),
+     * wake-on-lan/wake-on-wlan may not be available while in standby.
+     * Use {@link #isAllowedInLowPowerStandby(String)} to determine whether the device allows this
+     * feature to be used during Low Power Standby with the currently active Low Power Standby
+     * policy.
+     *
+     * @see #isAllowedInLowPowerStandby(String)
+     */
+    public static final String FEATURE_WAKE_ON_LAN_IN_LOW_POWER_STANDBY =
+            "com.android.lowpowerstandby.WAKE_ON_LAN";
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "LOW_POWER_STANDBY_ALLOWED_REASON_" }, flag = true, value = {
+            LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION,
+            LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST,
+            LOW_POWER_STANDBY_ALLOWED_REASON_ONGOING_CALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LowPowerStandbyAllowedReason {
+    }
+
+    /**
+     * Exempts active Voice Interaction Sessions in Low Power Standby.
+     *
+     * @see #isAllowedInLowPowerStandby(int)
+     */
+    public static final int LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION = 1 << 0;
+
+    /**
+     * Exempts apps on the temporary powersave allowlist.
+     *
+     * @see #isAllowedInLowPowerStandby(int)
+     */
+    public static final int LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST = 1 << 1;
+
+    /**
+     * Exempts apps with ongoing calls.
+     *
+     * <p>This includes apps with foreground services of type "phoneCall".
+     *
+     * @see #isAllowedInLowPowerStandby(int)
+     */
+    public static final int LOW_POWER_STANDBY_ALLOWED_REASON_ONGOING_CALL = 1 << 2;
+
+    /** @hide */
+    public static String lowPowerStandbyAllowedReasonsToString(
+            @LowPowerStandbyAllowedReason int allowedReasons) {
+        ArrayList<String> allowedStrings = new ArrayList<>();
+        if ((allowedReasons & LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION) != 0) {
+            allowedStrings.add("ALLOWED_REASON_VOICE_INTERACTION");
+            allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION;
+        }
+        if ((allowedReasons & LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST) != 0) {
+            allowedStrings.add("ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST");
+            allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST;
+        }
+        if ((allowedReasons & LOW_POWER_STANDBY_ALLOWED_REASON_ONGOING_CALL) != 0) {
+            allowedStrings.add("ALLOWED_REASON_ONGOING_CALL");
+            allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_ONGOING_CALL;
+        }
+        if (allowedReasons != 0) {
+            allowedStrings.add(String.valueOf(allowedReasons));
+        }
+        return String.join(",", allowedStrings);
+    }
+
+    /**
+     * Policy that defines the restrictions enforced by Low Power Standby.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class LowPowerStandbyPolicy {
+        /** Name of the policy, used for debugging & metrics */
+        @NonNull
+        private final String mIdentifier;
+
+        /** Packages that are exempt from Low Power Standby restrictions. */
+        @NonNull
+        private final Set<String> mExemptPackages;
+
+        /**
+         * Reasons that this policy allows apps to be automatically exempted
+         * from Low Power Standby restrictions for.
+         */
+        @LowPowerStandbyAllowedReason
+        private final int mAllowedReasons;
+
+        /**
+         * Features that are allowed to be used in Low Power Standby.
+         *
+         * @see #FEATURE_WAKE_ON_LAN_IN_LOW_POWER_STANDBY
+         */
+        @NonNull
+        private final Set<String> mAllowedFeatures;
+
+        /**
+         * Create a policy that defines the restrictions enforced by Low Power Standby.
+         *
+         * @param identifier Name of the policy, used for debugging & metrics.
+         * @param exemptPackages Packages that are exempt from Low Power Standby restrictions.
+         * @param allowedReasons Reasons that this policy allows apps to be automatically exempted
+         *                       from Low Power Standby restrictions for.
+         * @param allowedFeatures Features that are allowed to be used in Low Power Standby.
+         *                        Features are declared as strings, see
+         *                        {@link #FEATURE_WAKE_ON_LAN_IN_LOW_POWER_STANDBY} as an example.
+         */
+        public LowPowerStandbyPolicy(@NonNull String identifier,
+                @NonNull Set<String> exemptPackages,
+                @LowPowerStandbyAllowedReason int allowedReasons,
+                @NonNull Set<String> allowedFeatures) {
+            Objects.requireNonNull(identifier);
+            Objects.requireNonNull(exemptPackages);
+            Objects.requireNonNull(allowedFeatures);
+
+            mIdentifier = identifier;
+            mExemptPackages = Collections.unmodifiableSet(exemptPackages);
+            mAllowedReasons = allowedReasons;
+            mAllowedFeatures = Collections.unmodifiableSet(allowedFeatures);
+        }
+
+        @NonNull
+        public String getIdentifier() {
+            return mIdentifier;
+        }
+
+        @NonNull
+        public Set<String> getExemptPackages() {
+            return mExemptPackages;
+        }
+
+        @LowPowerStandbyAllowedReason
+        public int getAllowedReasons() {
+            return mAllowedReasons;
+        }
+
+        @NonNull
+        public Set<String> getAllowedFeatures() {
+            return mAllowedFeatures;
+        }
+
+        @Override
+        public String toString() {
+            return "Policy{"
+                    + "mIdentifier='" + mIdentifier + '\''
+                    + ", mExemptPackages=" + String.join(",", mExemptPackages)
+                    + ", mAllowedReasons=" + lowPowerStandbyAllowedReasonsToString(mAllowedReasons)
+                    + ", mAllowedFeatures=" + String.join(",", mAllowedFeatures)
+                    + '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof LowPowerStandbyPolicy)) return false;
+            LowPowerStandbyPolicy that = (LowPowerStandbyPolicy) o;
+            return mAllowedReasons == that.mAllowedReasons && Objects.equals(mIdentifier,
+                    that.mIdentifier) && Objects.equals(mExemptPackages, that.mExemptPackages)
+                    && Objects.equals(mAllowedFeatures, that.mAllowedFeatures);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mIdentifier, mExemptPackages, mAllowedReasons,
+                    mAllowedFeatures);
+        }
+
+        /** @hide */
+        public static IPowerManager.LowPowerStandbyPolicy toParcelable(
+                LowPowerStandbyPolicy policy) {
+            if (policy == null) {
+                return null;
+            }
+
+            IPowerManager.LowPowerStandbyPolicy parcelablePolicy =
+                    new IPowerManager.LowPowerStandbyPolicy();
+            parcelablePolicy.identifier = policy.mIdentifier;
+            parcelablePolicy.exemptPackages = new ArrayList<>(policy.mExemptPackages);
+            parcelablePolicy.allowedReasons = policy.mAllowedReasons;
+            parcelablePolicy.allowedFeatures = new ArrayList<>(policy.mAllowedFeatures);
+            return parcelablePolicy;
+        }
+
+        /** @hide */
+        public static LowPowerStandbyPolicy fromParcelable(
+                IPowerManager.LowPowerStandbyPolicy parcelablePolicy) {
+            if (parcelablePolicy == null) {
+                return null;
+            }
+
+            return new LowPowerStandbyPolicy(
+                    parcelablePolicy.identifier,
+                    new ArraySet<>(parcelablePolicy.exemptPackages),
+                    parcelablePolicy.allowedReasons,
+                    new ArraySet<>(parcelablePolicy.allowedFeatures));
+        }
+    }
+
+    /**
+     * Describes ports that may be requested to remain open during Low Power Standby.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class LowPowerStandbyPortDescription {
+        /** @hide */
+        @IntDef(prefix = { "PROTOCOL_" }, value = {
+                PROTOCOL_TCP,
+                PROTOCOL_UDP,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Protocol {
+        }
+
+        /**
+         * Constant to indicate the {@link LowPowerStandbyPortDescription} refers to a TCP port.
+         */
+        public static final int PROTOCOL_TCP = 6;
+        /**
+         * Constant to indicate the {@link LowPowerStandbyPortDescription} refers to a UDP port.
+         */
+        public static final int PROTOCOL_UDP = 17;
+
+        /** @hide */
+        @IntDef(prefix = { "MATCH_PORT_" }, value = {
+                MATCH_PORT_LOCAL,
+                MATCH_PORT_REMOTE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface PortMatcher {
+        }
+        /**
+         * Constant to indicate the {@link LowPowerStandbyPortDescription}'s port number is to be
+         * matched against the socket's local port number (the destination port number of an
+         * incoming packet).
+         */
+        public static final int MATCH_PORT_LOCAL = 1;
+        /**
+         * Constant to indicate the {@link LowPowerStandbyPortDescription}'s port number is to be
+         * matched against the socket's remote port number (the source port number of an
+         * incoming packet).
+         */
+        public static final int MATCH_PORT_REMOTE = 2;
+
+        @Protocol
+        private final int mProtocol;
+        @PortMatcher
+        private final int mPortMatcher;
+        private final int mPortNumber;
+        @Nullable
+        private final InetAddress mLocalAddress;
+
+        /**
+         * Describes a port.
+         *
+         * @param protocol The protocol of the port to match, {@link #PROTOCOL_TCP} or
+         *                 {@link #PROTOCOL_UDP}.
+         * @param portMatcher Whether to match the source port number of an incoming packet
+         *                    ({@link #MATCH_PORT_REMOTE}), or the destination port
+         *                    ({@link #MATCH_PORT_LOCAL}).
+         * @param portNumber The port number to match.
+         *
+         * @see #newLowPowerStandbyPortsLock(List)
+         */
+        public LowPowerStandbyPortDescription(@Protocol int protocol, @PortMatcher int portMatcher,
+                int portNumber) {
+            this.mProtocol = protocol;
+            this.mPortMatcher = portMatcher;
+            this.mPortNumber = portNumber;
+            this.mLocalAddress = null;
+        }
+
+        /**
+         * Describes a port.
+         *
+         * @param protocol The protocol of the port to match, {@link #PROTOCOL_TCP} or
+         *                 {@link #PROTOCOL_UDP}.
+         * @param portMatcher Whether to match the source port number of an incoming packet
+         *                    ({@link #MATCH_PORT_REMOTE}), or the destination port
+         *                    ({@link #MATCH_PORT_LOCAL}).
+         * @param portNumber The port number to match.
+         * @param localAddress The local address to match.
+         *
+         * @see #newLowPowerStandbyPortsLock(List)
+         */
+        public LowPowerStandbyPortDescription(@Protocol int protocol, @PortMatcher int portMatcher,
+                int portNumber, @Nullable InetAddress localAddress) {
+            this.mProtocol = protocol;
+            this.mPortMatcher = portMatcher;
+            this.mPortNumber = portNumber;
+            this.mLocalAddress = localAddress;
+        }
+
+        private String protocolToString(int protocol) {
+            switch (protocol) {
+                case PROTOCOL_TCP: return "TCP";
+                case PROTOCOL_UDP: return "UDP";
+            }
+            return String.valueOf(protocol);
+        }
+
+        private String portMatcherToString(int portMatcher) {
+            switch (portMatcher) {
+                case MATCH_PORT_LOCAL: return "MATCH_PORT_LOCAL";
+                case MATCH_PORT_REMOTE: return "MATCH_PORT_REMOTE";
+            }
+            return String.valueOf(portMatcher);
+        }
+
+        /**
+         * Returns the described port's protocol,
+         * either {@link #PROTOCOL_TCP} or {@link #PROTOCOL_UDP}.
+         *
+         * @see #PROTOCOL_TCP
+         * @see #PROTOCOL_UDP
+         * @see #getPortNumber()
+         * @see #getPortMatcher()
+         */
+        @Protocol
+        public int getProtocol() {
+            return mProtocol;
+        }
+
+        /**
+         * Returns how the port number ({@link #getPortNumber()}) should be matched against
+         * incoming packets.
+         * Either {@link #PROTOCOL_TCP} or {@link #PROTOCOL_UDP}.
+         *
+         * @see #PROTOCOL_TCP
+         * @see #PROTOCOL_UDP
+         * @see #getPortNumber()
+         * @see #getProtocol()
+         */
+        @PortMatcher
+        public int getPortMatcher() {
+            return mPortMatcher;
+        }
+
+        /**
+         * Returns how the port number that incoming packets should be matched against.
+         *
+         * @see #getPortMatcher()
+         * @see #getProtocol()
+         */
+        public int getPortNumber() {
+            return mPortNumber;
+        }
+
+        /**
+         * Returns the bind address to match against, or {@code null} if matching against any
+         * bind address.
+         *
+         * @see #getPortMatcher()
+         * @see #getProtocol()
+         */
+        @Nullable
+        public InetAddress getLocalAddress() {
+            return mLocalAddress;
+        }
+
+        @Override
+        public String toString() {
+            return "PortDescription{"
+                    + "mProtocol=" + protocolToString(mProtocol)
+                    + ", mPortMatcher=" + portMatcherToString(mPortMatcher)
+                    + ", mPortNumber=" + mPortNumber
+                    + ", mLocalAddress=" + mLocalAddress
+                    + '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof LowPowerStandbyPortDescription)) return false;
+            LowPowerStandbyPortDescription that = (LowPowerStandbyPortDescription) o;
+            return mProtocol == that.mProtocol && mPortMatcher == that.mPortMatcher
+                    && mPortNumber == that.mPortNumber && Objects.equals(mLocalAddress,
+                    that.mLocalAddress);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mProtocol, mPortMatcher, mPortNumber, mLocalAddress);
+        }
+
+        /** @hide */
+        public static IPowerManager.LowPowerStandbyPortDescription toParcelable(
+                LowPowerStandbyPortDescription portDescription) {
+            if (portDescription == null) {
+                return null;
+            }
+
+            IPowerManager.LowPowerStandbyPortDescription parcelablePortDescription =
+                    new IPowerManager.LowPowerStandbyPortDescription();
+            parcelablePortDescription.protocol = portDescription.mProtocol;
+            parcelablePortDescription.portMatcher = portDescription.mPortMatcher;
+            parcelablePortDescription.portNumber = portDescription.mPortNumber;
+            if (portDescription.mLocalAddress != null) {
+                parcelablePortDescription.localAddress = portDescription.mLocalAddress.getAddress();
+            }
+            return parcelablePortDescription;
+        }
+
+        /** @hide */
+        public static List<IPowerManager.LowPowerStandbyPortDescription> toParcelable(
+                List<LowPowerStandbyPortDescription> portDescriptions) {
+            if (portDescriptions == null) {
+                return null;
+            }
+
+            ArrayList<IPowerManager.LowPowerStandbyPortDescription> result = new ArrayList<>();
+            for (LowPowerStandbyPortDescription port : portDescriptions) {
+                result.add(toParcelable(port));
+            }
+            return result;
+        }
+
+        /** @hide */
+        public static LowPowerStandbyPortDescription fromParcelable(
+                IPowerManager.LowPowerStandbyPortDescription parcelablePortDescription) {
+            if (parcelablePortDescription == null) {
+                return null;
+            }
+
+            InetAddress localAddress = null;
+            if (parcelablePortDescription.localAddress != null) {
+                try {
+                    localAddress = InetAddress.getByAddress(parcelablePortDescription.localAddress);
+                } catch (UnknownHostException e) {
+                    Log.w(TAG, "Address has invalid length", e);
+                }
+            }
+            return new LowPowerStandbyPortDescription(
+                    parcelablePortDescription.protocol,
+                    parcelablePortDescription.portMatcher,
+                    parcelablePortDescription.portNumber,
+                    localAddress);
+        }
+
+        /** @hide */
+        public static List<LowPowerStandbyPortDescription> fromParcelable(
+                List<IPowerManager.LowPowerStandbyPortDescription> portDescriptions) {
+            if (portDescriptions == null) {
+                return null;
+            }
+
+            ArrayList<LowPowerStandbyPortDescription> result = new ArrayList<>();
+            for (IPowerManager.LowPowerStandbyPortDescription port : portDescriptions) {
+                result.add(fromParcelable(port));
+            }
+            return result;
+        }
+    }
+
+    /**
+     * An object that can be used to request network ports to remain open during Low Power Standby.
+     *
+     * <p>Use {@link #newLowPowerStandbyPortsLock} to create a ports lock, and {@link #acquire()}
+     * to request the ports to remain open. The request is only respected if the app requesting the
+     * lock is exempt from Low Power Standby ({@link #isExemptFromLowPowerStandby()}).
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("NotCloseable")
+    public final class LowPowerStandbyPortsLock {
+        private final IBinder mToken;
+        private final List<LowPowerStandbyPortDescription> mPorts;
+        private boolean mHeld;
+
+        LowPowerStandbyPortsLock(List<LowPowerStandbyPortDescription> ports) {
+            mPorts = ports;
+            mToken = new Binder();
+        }
+
+        /** Request the ports to remain open during standby. */
+        @RequiresPermission(android.Manifest.permission.SET_LOW_POWER_STANDBY_PORTS)
+        public void acquire() {
+            synchronized (mToken) {
+                try {
+                    mService.acquireLowPowerStandbyPorts(mToken,
+                            LowPowerStandbyPortDescription.toParcelable(mPorts));
+                    mHeld = true;
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        /**
+         * Release the request, allowing these ports to be blocked during standby.
+         *
+         * <p>Note: This lock is not reference counted, so calling this method will release the lock
+         * regardless of how many times {@link #acquire()} has been called before.
+         */
+        @RequiresPermission(android.Manifest.permission.SET_LOW_POWER_STANDBY_PORTS)
+        public void release() {
+            synchronized (mToken) {
+                try {
+                    mService.releaseLowPowerStandbyPorts(mToken);
+                    mHeld = false;
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        @Override
+        protected void finalize() {
+            synchronized (mToken) {
+                if (mHeld) {
+                    Log.wtf(TAG, "LowPowerStandbyPorts finalized while still held");
+                    release();
+                }
+            }
+        }
+    }
+
+    /**
+     * Constant for PreIdleTimeout normal mode (default mode, not short nor extend timeout) .
+     * @hide
+     */
+    public static final int PRE_IDLE_TIMEOUT_MODE_NORMAL = 0;
+
+    /**
+     * Constant for PreIdleTimeout long mode (extend timeout to keep in inactive mode
+     * longer).
+     * @hide
+     */
+    public static final int PRE_IDLE_TIMEOUT_MODE_LONG = 1;
+
+    /**
+     * Constant for PreIdleTimeout short mode (short timeout to go to doze mode quickly)
+     * @hide
+     */
+    public static final int PRE_IDLE_TIMEOUT_MODE_SHORT = 2;
+
+    /**
+     * A listener interface to get notified when the wakelock is enabled/disabled.
+     */
+    public interface WakeLockStateListener {
+        /**
+         * Frameworks could disable the wakelock because either device's power allowlist has
+         * changed, or the app's wakelock has exceeded its quota, or the app goes into cached
+         * state.
+         * <p>
+         * This callback is called whenever the wakelock's state has changed.
+         * </p>
+         *
+         * @param enabled true is enabled, false is disabled.
+         */
+        void onStateChanged(boolean enabled);
+    }
+
+    /**
+     * A wake lock is a mechanism to indicate that your application needs
+     * to have the device stay on.
+     * <p>
+     * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK}
+     * permission in an {@code <uses-permission>} element of the application's manifest.
+     * Obtain a wake lock by calling {@link PowerManager#newWakeLock(int, String)}.
+     * </p><p>
+     * Call {@link #acquire()} to acquire the wake lock and force the device to stay
+     * on at the level that was requested when the wake lock was created.
+     * </p><p>
+     * Call {@link #release()} when you are done and don't need the lock anymore.
+     * It is very important to do this as soon as possible to avoid running down the
+     * device's battery excessively.
+     * </p>
+     */
+    public final class WakeLock {
+        @UnsupportedAppUsage
+        private int mFlags;
+        @UnsupportedAppUsage
+        private String mTag;
+        private int mTagHash;
+        private final String mPackageName;
+        private final IBinder mToken;
+        private int mInternalCount;
+        private int mExternalCount;
+        private boolean mRefCounted = true;
+        private boolean mHeld;
+        private WorkSource mWorkSource;
+        private String mHistoryTag;
+        private final int mDisplayId;
+        private WakeLockStateListener mListener;
+        private IWakeLockCallback mCallback;
+
+        private final Runnable mReleaser = () -> release(RELEASE_FLAG_TIMEOUT);
+
+        WakeLock(int flags, String tag, String packageName, int displayId) {
+            mFlags = flags;
+            mTag = tag;
+            mTagHash = mTag.hashCode();
+            mPackageName = packageName;
+            mToken = new Binder();
+            mDisplayId = displayId;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            synchronized (mToken) {
+                if (mHeld) {
+                    Log.wtf(TAG, "WakeLock finalized while still held: " + mTag);
+                    Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_POWER,
+                            "WakeLocks", mTagHash);
+                    try {
+                        mService.releaseWakeLock(mToken, 0);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+
+        /**
+         * Sets whether this WakeLock is reference counted.
+         * <p>
+         * Wake locks are reference counted by default.  If a wake lock is
+         * reference counted, then each call to {@link #acquire()} must be
+         * balanced by an equal number of calls to {@link #release()}.  If a wake
+         * lock is not reference counted, then one call to {@link #release()} is
+         * sufficient to undo the effect of all previous calls to {@link #acquire()}.
+         * </p>
+         *
+         * @param value True to make the wake lock reference counted, false to
+         * make the wake lock non-reference counted.
+         */
+        public void setReferenceCounted(boolean value) {
+            synchronized (mToken) {
+                mRefCounted = value;
+            }
+        }
+
+        /**
+         * Acquires the wake lock.
+         * <p>
+         * Ensures that the device is on at the level requested when
+         * the wake lock was created.
+         * </p>
+         */
+        public void acquire() {
+            synchronized (mToken) {
+                acquireLocked();
+            }
+        }
+
+        /**
+         * Acquires the wake lock with a timeout.
+         * <p>
+         * Ensures that the device is on at the level requested when
+         * the wake lock was created.  The lock will be released after the given timeout
+         * expires.
+         * </p>
+         *
+         * @param timeout The timeout after which to release the wake lock, in milliseconds.
+         */
+        public void acquire(long timeout) {
+            synchronized (mToken) {
+                acquireLocked();
+                mHandler.postDelayed(mReleaser, timeout);
+            }
+        }
+
+        private void acquireLocked() {
+            mInternalCount++;
+            mExternalCount++;
+            if (!mRefCounted || mInternalCount == 1) {
+                // Do this even if the wake lock is already thought to be held (mHeld == true)
+                // because non-reference counted wake locks are not always properly released.
+                // For example, the keyguard's wake lock might be forcibly released by the
+                // power manager without the keyguard knowing.  A subsequent call to acquire
+                // should immediately acquire the wake lock once again despite never having
+                // been explicitly released by the keyguard.
+                mHandler.removeCallbacks(mReleaser);
+                Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_POWER,
+                        "WakeLocks", mTag, mTagHash);
+                try {
+                    mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
+                            mHistoryTag, mDisplayId, mCallback);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+                mHeld = true;
+            }
+        }
+
+        /**
+         * Releases the wake lock.
+         * <p>
+         * This method releases your claim to the CPU or screen being on.
+         * The screen may turn off shortly after you release the wake lock, or it may
+         * not if there are other wake locks still held.
+         * </p>
+         */
+        public void release() {
+            release(0);
+        }
+
+        /**
+         * Releases the wake lock with flags to modify the release behavior.
+         * <p>
+         * This method releases your claim to the CPU or screen being on.
+         * The screen may turn off shortly after you release the wake lock, or it may
+         * not if there are other wake locks still held.
+         * </p>
+         *
+         * @param flags Combination of flag values to modify the release behavior.
+         * Currently only {@link #RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY} is supported.
+         * Passing 0 is equivalent to calling {@link #release()}.
+         */
+        public void release(int flags) {
+            synchronized (mToken) {
+                if (mInternalCount > 0) {
+                    // internal count must only be decreased if it is > 0 or state of
+                    // the WakeLock object is broken.
+                    mInternalCount--;
+                }
+                if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
+                    mExternalCount--;
+                }
+                if (!mRefCounted || mInternalCount == 0) {
+                    mHandler.removeCallbacks(mReleaser);
+                    if (mHeld) {
+                        Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_POWER,
+                                "WakeLocks", mTagHash);
+                        try {
+                            mService.releaseWakeLock(mToken, flags);
+                        } catch (RemoteException e) {
+                            throw e.rethrowFromSystemServer();
+                        }
+                        mHeld = false;
+                    }
+                }
+                if (mRefCounted && mExternalCount < 0) {
+                    throw new RuntimeException("WakeLock under-locked " + mTag);
+                }
+            }
+        }
+
+        /**
+         * Returns true if the wake lock has been acquired but not yet released.
+         *
+         * @return True if the wake lock is held.
+         */
+        public boolean isHeld() {
+            synchronized (mToken) {
+                return mHeld;
+            }
+        }
+
+        /**
+         * Sets the work source associated with the wake lock.
+         * <p>
+         * The work source is used to determine on behalf of which application
+         * the wake lock is being held.  This is useful in the case where a
+         * service is performing work on behalf of an application so that the
+         * cost of that work can be accounted to the application.
+         * </p>
+         *
+         * <p>
+         * Make sure to follow the tag naming convention when using WorkSource
+         * to make it easier for app developers to understand wake locks
+         * attributed to them. See {@link PowerManager#newWakeLock(int, String)}
+         * documentation.
+         * </p>
+         *
+         * @param ws The work source, or null if none.
+         */
+        public void setWorkSource(WorkSource ws) {
+            synchronized (mToken) {
+                if (ws != null && ws.isEmpty()) {
+                    ws = null;
+                }
+
+                final boolean changed;
+                if (ws == null) {
+                    changed = mWorkSource != null;
+                    mWorkSource = null;
+                } else if (mWorkSource == null) {
+                    changed = true;
+                    mWorkSource = new WorkSource(ws);
+                } else {
+                    changed = !mWorkSource.equals(ws);
+                    if (changed) {
+                        mWorkSource.set(ws);
+                    }
+                }
+
+                if (changed && mHeld) {
+                    try {
+                        mService.updateWakeLockWorkSource(mToken, mWorkSource, mHistoryTag);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+
+        /** @hide */
+        public void setTag(String tag) {
+            mTag = tag;
+            mTagHash = mTag.hashCode();
+        }
+
+        /** @hide */
+        public String getTag() {
+            return mTag;
+        }
+
+        /** @hide */
+        public void setHistoryTag(String tag) {
+            mHistoryTag = tag;
+        }
+
+        /** @hide */
+        public void setUnimportantForLogging(boolean state) {
+            if (state) mFlags |= UNIMPORTANT_FOR_LOGGING;
+            else mFlags &= ~UNIMPORTANT_FOR_LOGGING;
+        }
+
+        @Override
+        public String toString() {
+            synchronized (mToken) {
+                return "WakeLock{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " held=" + mHeld + ", refCount=" + mInternalCount + "}";
+            }
+        }
+
+        /** @hide */
+        public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+            synchronized (mToken) {
+                final long token = proto.start(fieldId);
+                proto.write(PowerManagerProto.WakeLock.TAG, mTag);
+                proto.write(PowerManagerProto.WakeLock.PACKAGE_NAME, mPackageName);
+                proto.write(PowerManagerProto.WakeLock.HELD, mHeld);
+                proto.write(PowerManagerProto.WakeLock.INTERNAL_COUNT, mInternalCount);
+                if (mWorkSource != null) {
+                    mWorkSource.dumpDebug(proto, PowerManagerProto.WakeLock.WORK_SOURCE);
+                }
+                proto.end(token);
+            }
+        }
+
+        /**
+         * Wraps a Runnable such that this method immediately acquires the wake lock and then
+         * once the Runnable is done the wake lock is released.
+         *
+         * <p>Example:
+         *
+         * <pre>
+         * mHandler.post(mWakeLock.wrap(() -> {
+         *     // do things on handler, lock is held while we're waiting for this
+         *     // to get scheduled and until the runnable is done executing.
+         * });
+         * </pre>
+         *
+         * <p>Note: you must make sure that the Runnable eventually gets executed, otherwise you'll
+         *    leak the wakelock!
+         *
+         * @hide
+         */
+        @SuppressLint("WakelockTimeout")
+        public Runnable wrap(Runnable r) {
+            acquire();
+            return () -> {
+                try {
+                    r.run();
+                } finally {
+                    release();
+                }
+            };
+        }
+
+        /**
+         * Set the listener to get notified when the wakelock is enabled/disabled.
+         *
+         * @param executor {@link Executor} to handle listener callback.
+         * @param listener listener to be added, set the listener to null to cancel a listener.
+         */
+        public void setStateListener(@NonNull @CallbackExecutor Executor executor,
+                @Nullable WakeLockStateListener listener) {
+            Preconditions.checkNotNull(executor, "executor cannot be null");
+            synchronized (mToken) {
+                if (listener != mListener) {
+                    mListener = listener;
+                    if (listener != null) {
+                        mCallback = new IWakeLockCallback.Stub() {
+                            public void onStateChanged(boolean enabled) {
+                                final long token = Binder.clearCallingIdentity();
+                                try {
+                                    executor.execute(() -> {
+                                        listener.onStateChanged(enabled);
+                                    });
+                                } finally {
+                                    Binder.restoreCallingIdentity(token);
+                                }
+                            }
+                        };
+                    } else {
+                        mCallback = null;
+                    }
+                    if (mHeld) {
+                        try {
+                            mService.updateWakeLockCallback(mToken, mCallback);
+                        } catch (RemoteException e) {
+                            throw e.rethrowFromSystemServer();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static void invalidatePowerSaveModeCaches() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_IS_POWER_SAVE_MODE_PROPERTY);
+    }
+
+    /**
+     * @hide
+     */
+    public static void invalidateIsInteractiveCaches() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_IS_INTERACTIVE_PROPERTY);
+    }
+}
diff --git a/android-34/android/os/PowerManagerInternal.java b/android-34/android/os/PowerManagerInternal.java
new file mode 100644
index 0000000..8afd6de
--- /dev/null
+++ b/android-34/android/os/PowerManagerInternal.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2014 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.os;
+
+import android.view.Display;
+import android.view.KeyEvent;
+
+import java.util.function.Consumer;
+
+/**
+ * Power manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class PowerManagerInternal {
+    /**
+     * Wakefulness: The device is asleep.  It can only be awoken by a call to wakeUp().
+     * The screen should be off or in the process of being turned off by the display controller.
+     * The device typically passes through the dozing state first.
+     */
+    public static final int WAKEFULNESS_ASLEEP = 0;
+
+    /**
+     * Wakefulness: The device is fully awake.  It can be put to sleep by a call to goToSleep().
+     * When the user activity timeout expires, the device may start dreaming or go to sleep.
+     */
+    public static final int WAKEFULNESS_AWAKE = 1;
+
+    /**
+     * Wakefulness: The device is dreaming.  It can be awoken by a call to wakeUp(),
+     * which ends the dream.  The device goes to sleep when goToSleep() is called, when
+     * the dream ends or when unplugged.
+     * User activity may brighten the screen but does not end the dream.
+     */
+    public static final int WAKEFULNESS_DREAMING = 2;
+
+    /**
+     * Wakefulness: The device is dozing.  It is almost asleep but is allowing a special
+     * low-power "doze" dream to run which keeps the display on but lets the application
+     * processor be suspended.  It can be awoken by a call to wakeUp() which ends the dream.
+     * The device fully goes to sleep if the dream cannot be started or ends on its own.
+     */
+    public static final int WAKEFULNESS_DOZING = 3;
+
+    public static String wakefulnessToString(int wakefulness) {
+        switch (wakefulness) {
+            case WAKEFULNESS_ASLEEP:
+                return "Asleep";
+            case WAKEFULNESS_AWAKE:
+                return "Awake";
+            case WAKEFULNESS_DREAMING:
+                return "Dreaming";
+            case WAKEFULNESS_DOZING:
+                return "Dozing";
+            default:
+                return Integer.toString(wakefulness);
+        }
+    }
+
+    /**
+     * Converts platform constants to proto enums.
+     */
+    public static int wakefulnessToProtoEnum(int wakefulness) {
+        switch (wakefulness) {
+            case WAKEFULNESS_ASLEEP:
+                return PowerManagerInternalProto.WAKEFULNESS_ASLEEP;
+            case WAKEFULNESS_AWAKE:
+                return PowerManagerInternalProto.WAKEFULNESS_AWAKE;
+            case WAKEFULNESS_DREAMING:
+                return PowerManagerInternalProto.WAKEFULNESS_DREAMING;
+            case WAKEFULNESS_DOZING:
+                return PowerManagerInternalProto.WAKEFULNESS_DOZING;
+            default:
+                return wakefulness;
+        }
+    }
+
+    /**
+     * Returns true if the wakefulness state represents an interactive state
+     * as defined by {@link android.os.PowerManager#isInteractive}.
+     */
+    public static boolean isInteractive(int wakefulness) {
+        return wakefulness == WAKEFULNESS_AWAKE || wakefulness == WAKEFULNESS_DREAMING;
+    }
+
+    /**
+     * Used by the window manager to override the screen brightness based on the
+     * current foreground activity.
+     *
+     * This method must only be called by the window manager.
+     *
+     * @param brightness The overridden brightness, or Float.NaN to disable the override.
+     */
+    public abstract void setScreenBrightnessOverrideFromWindowManager(float brightness);
+
+    /**
+     * Used by the window manager to override the user activity timeout based on the
+     * current foreground activity.  It can only be used to make the timeout shorter
+     * than usual, not longer.
+     *
+     * This method must only be called by the window manager.
+     *
+     * @param timeoutMillis The overridden timeout, or -1 to disable the override.
+     */
+    public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis);
+
+    /**
+     * Used by the window manager to tell the power manager that the user is no longer actively
+     * using the device.
+     */
+    public abstract void setUserInactiveOverrideFromWindowManager();
+
+    /**
+     * Used by device administration to set the maximum screen off timeout.
+     *
+     * This method must only be called by the device administration policy manager.
+     */
+    public abstract void setMaximumScreenOffTimeoutFromDeviceAdmin(int userId, long timeMs);
+
+    /**
+     * Used by the dream manager to override certain properties while dozing.
+     *
+     * @param screenState The overridden screen state, or {@link Display#STATE_UNKNOWN}
+     * to disable the override.
+     * @param screenBrightness The overridden screen brightness, or
+     * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
+     */
+    public abstract void setDozeOverrideFromDreamManager(
+            int screenState, int screenBrightness);
+
+    /**
+     * Used by sidekick manager to tell the power manager if it shouldn't change the display state
+     * when a draw wake lock is acquired. Some processes may grab such a wake lock to do some work
+     * in a powered-up state, but we shouldn't give up sidekick control over the display until this
+     * override is lifted.
+     */
+    public abstract void setDrawWakeLockOverrideFromSidekick(boolean keepState);
+
+    public abstract PowerSaveState getLowPowerState(int serviceType);
+
+    public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
+
+    /**
+     * Same as {@link #registerLowPowerModeObserver} but can take a lambda.
+     */
+    public void registerLowPowerModeObserver(int serviceType, Consumer<PowerSaveState> listener) {
+        registerLowPowerModeObserver(new LowPowerModeListener() {
+            @Override
+            public int getServiceType() {
+                return serviceType;
+            }
+
+            @Override
+            public void onLowPowerModeChanged(PowerSaveState state) {
+                listener.accept(state);
+            }
+        });
+    }
+
+    public interface LowPowerModeListener {
+        int getServiceType();
+        void onLowPowerModeChanged(PowerSaveState state);
+    }
+
+    public abstract boolean setDeviceIdleMode(boolean enabled);
+
+    public abstract boolean setLightDeviceIdleMode(boolean enabled);
+
+    public abstract void setDeviceIdleWhitelist(int[] appids);
+
+    public abstract void setDeviceIdleTempWhitelist(int[] appids);
+
+    /**
+     * Updates the Low Power Standby allowlist.
+     *
+     * @param uids UIDs that are exempt from Low Power Standby restrictions
+     */
+    public abstract void setLowPowerStandbyAllowlist(int[] uids);
+
+    /**
+     * Used by LowPowerStandbyController to notify the power manager that Low Power Standby's
+     * active state has changed.
+     *
+     * @param active {@code true} to activate Low Power Standby, {@code false} to turn it off.
+     */
+    public abstract void setLowPowerStandbyActive(boolean active);
+
+    public abstract void startUidChanges();
+
+    public abstract void finishUidChanges();
+
+    public abstract void updateUidProcState(int uid, int procState);
+
+    public abstract void uidGone(int uid);
+
+    public abstract void uidActive(int uid);
+
+    public abstract void uidIdle(int uid);
+
+    /**
+     * Boost: It is sent when user interacting with the device, for example,
+     * touchscreen events are incoming.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Boost.aidl
+     */
+    public static final int BOOST_INTERACTION = 0;
+
+    /**
+     * Boost: It indicates that the framework is likely to provide a new display
+     * frame soon. This implies that the device should ensure that the display
+     * processing path is powered up and ready to receive that update.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Boost.aidl
+     */
+    public static final int BOOST_DISPLAY_UPDATE_IMMINENT = 1;
+
+    /**
+     * SetPowerBoost() indicates the device may need to boost some resources, as
+     * the load is likely to increase before the kernel governors can react.
+     * Depending on the boost, it may be appropriate to raise the frequencies of
+     * CPU, GPU, memory subsystem, or stop CPU from going into deep sleep state.
+     *
+     * @param boost Boost which is to be set with a timeout.
+     * @param durationMs The expected duration of the user's interaction, if
+     *        known, or 0 if the expected duration is unknown.
+     *        a negative value indicates canceling previous boost.
+     *        A given platform can choose to boost some time based on durationMs,
+     *        and may also pick an appropriate timeout for 0 case.
+     */
+    public abstract void setPowerBoost(int boost, int durationMs);
+
+    /**
+     * Mode: It indicates that the device is to allow wake up when the screen
+     * is tapped twice.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_DOUBLE_TAP_TO_WAKE = 0;
+
+    /**
+     * Mode: It indicates Low power mode is activated or not. Low power mode
+     * is intended to save battery at the cost of performance.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_LOW_POWER = 1;
+
+    /**
+     * Mode: It indicates Sustained Performance mode is activated or not.
+     * Sustained performance mode is intended to provide a consistent level of
+     * performance for a prolonged amount of time.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_SUSTAINED_PERFORMANCE = 2;
+
+    /**
+     * Mode: It sets the device to a fixed performance level which can be sustained
+     * under normal indoor conditions for at least 10 minutes.
+     * Fixed performance mode puts both upper and lower bounds on performance such
+     * that any workload run while in a fixed performance mode should complete in
+     * a repeatable amount of time.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_FIXED_PERFORMANCE = 3;
+
+    /**
+     * Mode: It indicates VR Mode is activated or not. VR mode is intended to
+     * provide minimum guarantee for performance for the amount of time the device
+     * can sustain it.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_VR = 4;
+
+    /**
+     * Mode: It indicates that an application has been launched.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_LAUNCH = 5;
+
+    /**
+     * Mode: It indicates that the device is about to enter a period of expensive
+     * rendering.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_EXPENSIVE_RENDERING = 6;
+
+    /**
+     * Mode: It indicates that the device is about entering/leaving interactive
+     * state or on-interactive state.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_INTERACTIVE = 7;
+
+    /**
+     * Mode: It indicates the device is in device idle, externally known as doze.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_DEVICE_IDLE = 8;
+
+    /**
+     * Mode: It indicates that display is either off or still on but is optimized
+     * for low power.
+     * Defined in hardware/interfaces/power/aidl/android/hardware/power/Mode.aidl
+     */
+    public static final int MODE_DISPLAY_INACTIVE = 9;
+
+    /**
+     * SetPowerMode() is called to enable/disable specific hint mode, which
+     * may result in adjustment of power/performance parameters of the
+     * cpufreq governor and other controls on device side.
+     *
+     * @param mode Mode which is to be enable/disable.
+     * @param enabled true to enable, false to disable the mode.
+     */
+    public abstract void setPowerMode(int mode, boolean enabled);
+
+    /** Returns whether there hasn't been a user activity event for the given number of ms. */
+    public abstract boolean wasDeviceIdleFor(long ms);
+
+    /** Returns information about the last wakeup event. */
+    public abstract PowerManager.WakeData getLastWakeup();
+
+    /** Returns information about the last event to go to sleep. */
+    public abstract PowerManager.SleepData getLastGoToSleep();
+
+    /** Allows power button to intercept a power key button press. */
+    public abstract boolean interceptPowerKeyDown(KeyEvent event);
+
+    /**
+     * Internal version of {@link android.os.PowerManager#nap} which allows for napping while the
+     * device is not awake.
+     */
+    public abstract void nap(long eventTime, boolean allowWake);
+
+    /**
+     * Returns true if ambient display is suppressed by any app with any token. This method will
+     * return false if ambient display is not available.
+     */
+    public abstract boolean isAmbientDisplaySuppressed();
+}
diff --git a/android-34/android/os/PowerSaveState.java b/android-34/android/os/PowerSaveState.java
new file mode 100644
index 0000000..7d3cd06
--- /dev/null
+++ b/android-34/android/os/PowerSaveState.java
@@ -0,0 +1,124 @@
+/* Copyright 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.os;
+
+/**
+ * Data class for battery saver state. It contains the data
+ * <p>
+ * 1. Whether battery saver mode is enabled
+ * 2. Specific parameters to use in battery saver mode (i.e. screen brightness, location mode)
+ *
+ * @hide
+ */
+public class PowerSaveState implements Parcelable {
+    /**
+     * Whether we should enable battery saver for this service.
+     *
+     * @see com.android.server.power.batterysaver.BatterySaverPolicy
+     */
+    public final boolean batterySaverEnabled;
+    /**
+     * Whether the battery saver is enabled globally, which means the data we get from
+     * {@link PowerManager#isPowerSaveMode()}
+     */
+    public final boolean globalBatterySaverEnabled;
+    public final int locationMode;
+    public final int soundTriggerMode;
+    public final float brightnessFactor;
+
+    public PowerSaveState(Builder builder) {
+        batterySaverEnabled = builder.mBatterySaverEnabled;
+        locationMode = builder.mLocationMode;
+        soundTriggerMode = builder.mSoundTriggerMode;
+        brightnessFactor = builder.mBrightnessFactor;
+        globalBatterySaverEnabled = builder.mGlobalBatterySaverEnabled;
+    }
+
+    public PowerSaveState(Parcel in) {
+        batterySaverEnabled = in.readByte() != 0;
+        globalBatterySaverEnabled = in.readByte() != 0;
+        locationMode = in.readInt();
+        soundTriggerMode = in.readInt();
+        brightnessFactor = in.readFloat();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte((byte) (batterySaverEnabled ? 1 : 0));
+        dest.writeByte((byte) (globalBatterySaverEnabled ? 1 : 0));
+        dest.writeInt(locationMode);
+        dest.writeInt(soundTriggerMode);
+        dest.writeFloat(brightnessFactor);
+    }
+
+    public static final class Builder {
+        private boolean mBatterySaverEnabled = false;
+        private boolean mGlobalBatterySaverEnabled = false;
+        private int mLocationMode = 0;
+        private int mSoundTriggerMode = PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED;
+        private float mBrightnessFactor = 0.5f;
+
+        public Builder() {}
+
+        public Builder setBatterySaverEnabled(boolean enabled) {
+            mBatterySaverEnabled = enabled;
+            return this;
+        }
+
+        public Builder setGlobalBatterySaverEnabled(boolean enabled) {
+            mGlobalBatterySaverEnabled = enabled;
+            return this;
+        }
+
+        public Builder setSoundTriggerMode(int mode) {
+            mSoundTriggerMode = mode;
+            return this;
+        }
+
+        public Builder setLocationMode(int mode) {
+            mLocationMode = mode;
+            return this;
+        }
+
+        public Builder setBrightnessFactor(float factor) {
+            mBrightnessFactor = factor;
+            return this;
+        }
+
+        public PowerSaveState build() {
+            return new PowerSaveState(this);
+        }
+    }
+
+    public static final Parcelable.Creator<PowerSaveState>
+            CREATOR = new Parcelable.Creator<PowerSaveState>() {
+
+        @Override
+        public PowerSaveState createFromParcel(Parcel source) {
+            return new PowerSaveState(source);
+        }
+
+        @Override
+        public PowerSaveState[] newArray(int size) {
+            return new PowerSaveState[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/android-34/android/os/PowerWhitelistManager.java b/android-34/android/os/PowerWhitelistManager.java
new file mode 100644
index 0000000..4ce31e9
--- /dev/null
+++ b/android-34/android/os/PowerWhitelistManager.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Interface to access and modify the permanent and temporary power save whitelist. The two lists
+ * are kept separately. Apps placed on the permanent whitelist are only removed via an explicit
+ * removeFromWhitelist call. Apps whitelisted by default by the system cannot be removed. Apps
+ * placed on the temporary whitelist are removed from that whitelist after a predetermined amount of
+ * time.
+ *
+ * @deprecated Use {@link PowerExemptionManager} instead
+ * @hide
+ */
+@SystemApi
+@Deprecated
+@SystemService(Context.POWER_WHITELIST_MANAGER)
+public class PowerWhitelistManager {
+    private final Context mContext;
+    // Proxy to DeviceIdleController for now
+    // TODO: migrate to PowerWhitelistController
+    private final IDeviceIdleController mService;
+
+    private final PowerExemptionManager mPowerExemptionManager;
+
+    /**
+     * Indicates that an unforeseen event has occurred and the app should be whitelisted to handle
+     * it.
+     */
+    public static final int EVENT_UNSPECIFIED = PowerExemptionManager.EVENT_UNSPECIFIED;
+
+    /**
+     * Indicates that an SMS event has occurred and the app should be whitelisted to handle it.
+     */
+    public static final int EVENT_SMS = PowerExemptionManager.EVENT_SMS;
+
+    /**
+     * Indicates that an MMS event has occurred and the app should be whitelisted to handle it.
+     */
+    public static final int EVENT_MMS = PowerExemptionManager.EVENT_MMS;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EVENT_"}, value = {
+            EVENT_UNSPECIFIED,
+            EVENT_SMS,
+            EVENT_MMS,
+    })
+    public @interface WhitelistEvent {
+    }
+
+    /**
+     * Allow the temp allowlist behavior, plus allow foreground service start from background.
+     */
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+            PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+    /**
+     * Only allow the temp allowlist behavior, not allow foreground service start from
+     * background.
+     */
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+            PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
+
+    /**
+     * The list of temp allowlist types.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "TEMPORARY_ALLOWLIST_TYPE_" }, value = {
+            TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+            TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TempAllowListType {}
+
+    /* Reason code for BG-FGS-launch. */
+    /**
+     * BG-FGS-launch is denied.
+     * @hide
+     */
+    public static final int REASON_DENIED = PowerExemptionManager.REASON_DENIED;
+
+    /* Reason code range 0-9 are reserved for default reasons */
+    /**
+     * The default reason code if reason is unknown.
+     */
+    public static final int REASON_UNKNOWN = PowerExemptionManager.REASON_UNKNOWN;
+    /**
+     * Use REASON_OTHER if there is no better choice.
+     */
+    public static final int REASON_OTHER = PowerExemptionManager.REASON_OTHER;
+
+    /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT =
+            PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT_UI =
+            PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
+    /** @hide */
+    public static final int REASON_PROC_STATE_TOP = PowerExemptionManager.REASON_PROC_STATE_TOP;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BTOP = PowerExemptionManager.REASON_PROC_STATE_BTOP;
+    /** @hide */
+    public static final int REASON_PROC_STATE_FGS = PowerExemptionManager.REASON_PROC_STATE_FGS;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BFGS = PowerExemptionManager.REASON_PROC_STATE_BFGS;
+
+    /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
+    /** @hide */
+    public static final int REASON_UID_VISIBLE = PowerExemptionManager.REASON_UID_VISIBLE;
+    /** @hide */
+    public static final int REASON_SYSTEM_UID = PowerExemptionManager.REASON_SYSTEM_UID;
+    /** @hide */
+    public static final int REASON_ACTIVITY_STARTER = PowerExemptionManager.REASON_ACTIVITY_STARTER;
+    /** @hide */
+    public static final int REASON_START_ACTIVITY_FLAG =
+            PowerExemptionManager.REASON_START_ACTIVITY_FLAG;
+    /** @hide */
+    public static final int REASON_FGS_BINDING = PowerExemptionManager.REASON_FGS_BINDING;
+    /** @hide */
+    public static final int REASON_DEVICE_OWNER = PowerExemptionManager.REASON_DEVICE_OWNER;
+    /** @hide */
+    public static final int REASON_PROFILE_OWNER = PowerExemptionManager.REASON_PROFILE_OWNER;
+    /** @hide */
+    public static final int REASON_COMPANION_DEVICE_MANAGER =
+            PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
+    /**
+     * START_ACTIVITIES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION =
+            PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
+    /**
+     * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_FGS_PERMISSION =
+            PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION =
+            PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION =
+            PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
+    /** @hide */
+    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION =
+            PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+    /** @hide */
+    public static final int REASON_DEVICE_DEMO_MODE = PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
+    /** @hide */
+    public static final int REASON_ALLOWLISTED_PACKAGE =
+            PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
+    /** @hide */
+    public static final int REASON_APPOP = PowerExemptionManager.REASON_APPOP;
+
+    /* BG-FGS-launch is allowed by temp-allowlist or system-allowlist.
+       Reason code for temp and system allowlist starts here.
+       Reason code range 100-199 are reserved for public reasons. */
+    /**
+     * Set temp-allowlist for location geofence purpose.
+     */
+    public static final int REASON_GEOFENCING = PowerExemptionManager.REASON_GEOFENCING;
+    /**
+     * Set temp-allowlist for server push messaging.
+     */
+    public static final int REASON_PUSH_MESSAGING = PowerExemptionManager.REASON_PUSH_MESSAGING;
+    /**
+     * Set temp-allowlist for server push messaging over the quota.
+     */
+    public static final int REASON_PUSH_MESSAGING_OVER_QUOTA =
+            PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA;
+    /**
+     * Set temp-allowlist for activity recognition.
+     */
+    public static final int REASON_ACTIVITY_RECOGNITION =
+            PowerExemptionManager.REASON_ACTIVITY_RECOGNITION;
+
+    /* Reason code range 200-299 are reserved for broadcast actions */
+    /**
+     * Broadcast ACTION_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_BOOT_COMPLETED = PowerExemptionManager.REASON_BOOT_COMPLETED;
+    /**
+     * Broadcast ACTION_PRE_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_PRE_BOOT_COMPLETED =
+            PowerExemptionManager.REASON_PRE_BOOT_COMPLETED;
+    /**
+     * Broadcast ACTION_LOCKED_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_LOCKED_BOOT_COMPLETED =
+            PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
+
+    /* Reason code range 300-399 are reserved for other internal reasons */
+    /**
+     * Device idle system allowlist, including EXCEPT-IDLE
+     * @hide
+     */
+    public static final int REASON_SYSTEM_ALLOW_LISTED =
+            PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
+    /** @hide */
+    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK =
+            PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK;
+    /**
+     * AlarmManagerService.
+     * @hide
+     */
+    public static final int REASON_ALARM_MANAGER_WHILE_IDLE =
+            PowerExemptionManager.REASON_ALARM_MANAGER_WHILE_IDLE;
+    /**
+     * ActiveServices.
+     * @hide
+     */
+    public static final int REASON_SERVICE_LAUNCH = PowerExemptionManager.REASON_SERVICE_LAUNCH;
+    /**
+     * KeyChainSystemService.
+     * @hide
+     */
+    public static final int REASON_KEY_CHAIN = PowerExemptionManager.REASON_KEY_CHAIN;
+    /**
+     * PackageManagerService.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_VERIFIER = PowerExemptionManager.REASON_PACKAGE_VERIFIER;
+    /**
+     * SyncManager.
+     * @hide
+     */
+    public static final int REASON_SYNC_MANAGER = PowerExemptionManager.REASON_SYNC_MANAGER;
+    /**
+     * DomainVerificationProxyV1.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V1 =
+            PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V1;
+    /**
+     * DomainVerificationProxyV2.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V2 =
+            PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V2;
+    /** @hide */
+    public static final int REASON_VPN = 309;
+    /**
+     * NotificationManagerService.
+     * @hide
+     */
+    public static final int REASON_NOTIFICATION_SERVICE =
+            PowerExemptionManager.REASON_NOTIFICATION_SERVICE;
+    /**
+     * Broadcast ACTION_MY_PACKAGE_REPLACED.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_REPLACED = PowerExemptionManager.REASON_PACKAGE_REPLACED;
+    /**
+     * LocationProvider.
+     * @hide
+     */
+    @SystemApi
+    public static final int REASON_LOCATION_PROVIDER =
+            PowerExemptionManager.REASON_LOCATION_PROVIDER;
+    /**
+     * MediaButtonReceiver.
+     * @hide
+     */
+    public static final int REASON_MEDIA_BUTTON = PowerExemptionManager.REASON_MEDIA_BUTTON;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_SMS = PowerExemptionManager.REASON_EVENT_SMS;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_MMS = PowerExemptionManager.REASON_EVENT_MMS;
+    /**
+     * Shell app.
+     * @hide
+     */
+    public static final int REASON_SHELL = PowerExemptionManager.REASON_SHELL;
+
+    /**
+     * The list of BG-FGS-Launch and temp-allowlist reason code.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "REASON_" }, value = {
+            // BG-FGS-Launch reasons.
+            REASON_DENIED,
+            REASON_UNKNOWN,
+            REASON_OTHER,
+            REASON_PROC_STATE_PERSISTENT,
+            REASON_PROC_STATE_PERSISTENT_UI,
+            REASON_PROC_STATE_TOP,
+            REASON_PROC_STATE_BTOP,
+            REASON_PROC_STATE_FGS,
+            REASON_PROC_STATE_BFGS,
+            REASON_UID_VISIBLE,
+            REASON_SYSTEM_UID,
+            REASON_ACTIVITY_STARTER,
+            REASON_START_ACTIVITY_FLAG,
+            REASON_FGS_BINDING,
+            REASON_DEVICE_OWNER,
+            REASON_PROFILE_OWNER,
+            REASON_COMPANION_DEVICE_MANAGER,
+            REASON_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_BACKGROUND_FGS_PERMISSION,
+            REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_INSTR_BACKGROUND_FGS_PERMISSION,
+            REASON_SYSTEM_ALERT_WINDOW_PERMISSION,
+            REASON_DEVICE_DEMO_MODE,
+            REASON_ALLOWLISTED_PACKAGE,
+            REASON_APPOP,
+            // temp and system allowlist reasons.
+            REASON_GEOFENCING,
+            REASON_PUSH_MESSAGING,
+            REASON_PUSH_MESSAGING_OVER_QUOTA,
+            REASON_ACTIVITY_RECOGNITION,
+            REASON_BOOT_COMPLETED,
+            REASON_PRE_BOOT_COMPLETED,
+            REASON_LOCKED_BOOT_COMPLETED,
+            REASON_SYSTEM_ALLOW_LISTED,
+            REASON_ALARM_MANAGER_ALARM_CLOCK,
+            REASON_ALARM_MANAGER_WHILE_IDLE,
+            REASON_SERVICE_LAUNCH,
+            REASON_KEY_CHAIN,
+            REASON_PACKAGE_VERIFIER,
+            REASON_SYNC_MANAGER,
+            REASON_DOMAIN_VERIFICATION_V1,
+            REASON_DOMAIN_VERIFICATION_V2,
+            REASON_VPN,
+            REASON_NOTIFICATION_SERVICE,
+            REASON_PACKAGE_REPLACED,
+            REASON_LOCATION_PROVIDER,
+            REASON_MEDIA_BUTTON,
+            REASON_EVENT_SMS,
+            REASON_EVENT_MMS,
+            REASON_SHELL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReasonCode {}
+
+    /**
+     * @hide
+     */
+    public PowerWhitelistManager(@NonNull Context context) {
+        mContext = context;
+        mService = context.getSystemService(DeviceIdleManager.class).getService();
+        mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
+    }
+
+    /**
+     * Add the specified package to the permanent power save whitelist.
+     *
+     * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(String)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void addToWhitelist(@NonNull String packageName) {
+        mPowerExemptionManager.addToPermanentAllowList(packageName);
+    }
+
+    /**
+     * Add the specified packages to the permanent power save whitelist.
+     *
+     * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(List)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void addToWhitelist(@NonNull List<String> packageNames) {
+        mPowerExemptionManager.addToPermanentAllowList(packageNames);
+    }
+
+    /**
+     * Get a list of app IDs of app that are whitelisted. This does not include temporarily
+     * whitelisted apps.
+     *
+     * @param includingIdle Set to true if the app should be whitelisted from device idle as well
+     *                      as other power save restrictions
+     * @deprecated Use {@link PowerExemptionManager#getAllowListedAppIds(boolean)} instead
+     * @hide
+     */
+    @Deprecated
+    @NonNull
+    public int[] getWhitelistedAppIds(boolean includingIdle) {
+        return mPowerExemptionManager.getAllowListedAppIds(includingIdle);
+    }
+
+    /**
+     * Returns true if the app is whitelisted from power save restrictions. This does not include
+     * temporarily whitelisted apps.
+     *
+     * @param includingIdle Set to true if the app should be whitelisted from device
+     *                      idle as well as other power save restrictions
+     * @deprecated Use {@link PowerExemptionManager#isAllowListed(String, boolean)} instead
+     * @hide
+     */
+    @Deprecated
+    public boolean isWhitelisted(@NonNull String packageName, boolean includingIdle) {
+        return mPowerExemptionManager.isAllowListed(packageName, includingIdle);
+    }
+
+    /**
+     * Remove an app from the permanent power save whitelist. Only apps that were added via
+     * {@link #addToWhitelist(String)} or {@link #addToWhitelist(List)} will be removed. Apps
+     * whitelisted by default by the system cannot be removed.
+     *
+     * @param packageName The app to remove from the whitelist
+     * @deprecated Use {@link PowerExemptionManager#removeFromPermanentAllowList(String)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void removeFromWhitelist(@NonNull String packageName) {
+        mPowerExemptionManager.removeFromPermanentAllowList(packageName);
+    }
+
+    /**
+     * Add an app to the temporary whitelist for a short amount of time.
+     *
+     * @param packageName The package to add to the temp whitelist
+     * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
+     * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason a optional human readable reason string, could be null or empty string.
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+     *             String, int, String, long)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public void whitelistAppTemporarily(@NonNull String packageName, long durationMs,
+            @ReasonCode int reasonCode, @Nullable String reason) {
+        mPowerExemptionManager.addToTemporaryAllowList(packageName, reasonCode, reason, durationMs);
+    }
+
+    /**
+     * Add an app to the temporary whitelist for a short amount of time.
+     *
+     * @param packageName The package to add to the temp whitelist
+     * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+     *             String, int, String, long)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
+        mPowerExemptionManager.addToTemporaryAllowList(
+                packageName, REASON_UNKNOWN, packageName, durationMs);
+    }
+
+    /**
+     * Add an app to the temporary whitelist for a short amount of time for a specific reason. The
+     * temporary whitelist is kept separately from the permanent whitelist and apps are
+     * automatically removed from the temporary whitelist after a predetermined amount of time.
+     *
+     * @param packageName The package to add to the temp whitelist
+     * @param event       The reason to add the app to the temp whitelist
+     * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
+     *                    used for logging purposes. Could be null or empty string.
+     * @return The duration (in milliseconds) that the app is whitelisted for
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+     *             String, int, String, int)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
+            @WhitelistEvent int event, @Nullable String reason) {
+        return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+                packageName, REASON_UNKNOWN, reason, event);
+    }
+
+    /**
+     * Add an app to the temporary whitelist for a short amount of time for a specific reason. The
+     * temporary whitelist is kept separately from the permanent whitelist and apps are
+     * automatically removed from the temporary whitelist after a predetermined amount of time.
+     *
+     * @param packageName The package to add to the temp whitelist
+     * @param event       The reason to add the app to the temp whitelist
+     * @param reasonCode  one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
+     *                    used for logging purposes. Could be null or empty string.
+     * @return The duration (in milliseconds) that the app is whitelisted for
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+     *             String, int, String, int)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
+            @WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
+        return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+                packageName, reasonCode, reason, event);
+    }
+
+    /**
+     * @hide
+     *
+     * @deprecated Use {@link PowerExemptionManager#getReasonCodeFromProcState(int)} instead
+     */
+    @Deprecated
+    public static @ReasonCode int getReasonCodeFromProcState(int procState) {
+        return PowerExemptionManager.getReasonCodeFromProcState(procState);
+    }
+
+    /**
+     * Return string name of the integer reason code.
+     * @hide
+     * @param reasonCode
+     * @return string name of the reason code.
+     * @deprecated Use {@link PowerExemptionManager#reasonCodeToString(int)} instead
+     */
+    @Deprecated
+    public static String reasonCodeToString(@ReasonCode int reasonCode) {
+        return PowerExemptionManager.reasonCodeToString(reasonCode);
+    }
+}
diff --git a/android-34/android/os/Process.java b/android-34/android/os/Process.java
new file mode 100644
index 0000000..04525e8
--- /dev/null
+++ b/android-34/android/os/Process.java
@@ -0,0 +1,1673 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UptimeMillisLong;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build.VERSION_CODES;
+import android.sysprop.MemoryProperties;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructPollfd;
+import android.util.Pair;
+import android.webkit.WebViewZygote;
+
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tools for managing OS processes.
+ */
+public class Process {
+    private static final String LOG_TAG = "Process";
+
+    /**
+     * An invalid UID value.
+     */
+    public static final int INVALID_UID = -1;
+
+    /**
+     * Defines the root UID.
+     */
+    public static final int ROOT_UID = 0;
+
+    /**
+     * Defines the UID/GID under which system code runs.
+     */
+    public static final int SYSTEM_UID = 1000;
+
+    /**
+     * Defines the UID/GID under which the telephony code runs.
+     */
+    public static final int PHONE_UID = 1001;
+
+    /**
+     * Defines the UID/GID for the user shell.
+     */
+    public static final int SHELL_UID = 2000;
+
+    /**
+     * Defines the UID/GID for the log group.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int LOG_UID = 1007;
+
+    /**
+     * Defines the UID/GID for the WIFI native processes like wificond, supplicant, hostapd,
+     * vendor HAL, etc.
+     */
+    public static final int WIFI_UID = 1010;
+
+    /**
+     * Defines the UID/GID for the mediaserver process.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int MEDIA_UID = 1013;
+
+    /**
+     * Defines the UID/GID for the DRM process.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int DRM_UID = 1019;
+
+    /**
+     * Defines the GID for the group that allows write access to the internal media storage.
+     * @hide
+     */
+    public static final int SDCARD_RW_GID = 1015;
+
+    /**
+     * Defines the UID/GID for the group that controls VPN services.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int VPN_UID = 1016;
+
+    /**
+     * Defines the UID/GID for keystore.
+     * @hide
+     */
+    public static final int KEYSTORE_UID = 1017;
+
+    /**
+     * Defines the UID/GID for credstore.
+     * @hide
+     */
+    public static final int CREDSTORE_UID = 1076;
+
+    /**
+     * Defines the UID/GID for the NFC service process.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int NFC_UID = 1027;
+
+    /**
+     * Defines the UID/GID for the clatd process.
+     * @hide
+     * */
+    public static final int CLAT_UID = 1029;
+
+    /**
+     * Defines the UID/GID for the Bluetooth service process.
+     */
+    public static final int BLUETOOTH_UID = 1002;
+
+    /**
+     * Defines the GID for the group that allows write access to the internal media storage.
+     * @hide
+     */
+    public static final int MEDIA_RW_GID = 1023;
+
+    /**
+     * Access to installed package details
+     * @hide
+     */
+    public static final int PACKAGE_INFO_GID = 1032;
+
+    /**
+     * Defines the UID/GID for the shared RELRO file updater process.
+     * @hide
+     */
+    public static final int SHARED_RELRO_UID = 1037;
+
+    /**
+     * Defines the UID/GID for the audioserver process.
+     * @hide
+     */
+    public static final int AUDIOSERVER_UID = 1041;
+
+    /**
+     * Defines the UID/GID for the cameraserver process
+     * @hide
+     */
+    public static final int CAMERASERVER_UID = 1047;
+
+    /**
+     * Defines the UID/GID for the tethering DNS resolver (currently dnsmasq).
+     * @hide
+     */
+    public static final int DNS_TETHER_UID = 1052;
+
+    /**
+     * Defines the UID/GID for the WebView zygote process.
+     * @hide
+     */
+    public static final int WEBVIEW_ZYGOTE_UID = 1053;
+
+    /**
+     * Defines the UID used for resource tracking for OTA updates.
+     * @hide
+     */
+    public static final int OTA_UPDATE_UID = 1061;
+
+    /**
+     * Defines the UID used for statsd
+     * @hide
+     */
+    public static final int STATSD_UID = 1066;
+
+    /**
+     * Defines the UID used for incidentd.
+     * @hide
+     */
+    public static final int INCIDENTD_UID = 1067;
+
+    /**
+     * Defines the UID/GID for the Secure Element service process.
+     * @hide
+     */
+    public static final int SE_UID = 1068;
+
+    /**
+     * Defines the UID/GID for the NetworkStack app.
+     * @hide
+     */
+    public static final int NETWORK_STACK_UID = 1073;
+
+    /**
+     * Defines the UID/GID for fs-verity certificate ownership in keystore.
+     * @hide
+     */
+    public static final int FSVERITY_CERT_UID = 1075;
+
+    /**
+     * GID that gives access to USB OTG (unreliable) volumes on /mnt/media_rw/<vol name>
+     * @hide
+     */
+    public static final int EXTERNAL_STORAGE_GID = 1077;
+
+    /**
+     * GID that gives write access to app-private data directories on external
+     * storage (used on devices without sdcardfs only).
+     * @hide
+     */
+    public static final int EXT_DATA_RW_GID = 1078;
+
+    /**
+     * GID that gives write access to app-private OBB directories on external
+     * storage (used on devices without sdcardfs only).
+     * @hide
+     */
+    public static final int EXT_OBB_RW_GID = 1079;
+
+    /**
+     * Defines the UID/GID for the Uwb service process.
+     * @hide
+     */
+    public static final int UWB_UID = 1083;
+
+    /**
+     * Defines a virtual UID that is used to aggregate data related to SDK sandbox UIDs.
+     * {@see SdkSandboxManager}
+     * @hide
+     */
+    @TestApi
+    public static final int SDK_SANDBOX_VIRTUAL_UID = 1090;
+
+    /**
+     * GID that corresponds to the INTERNET permission.
+     * Must match the value of AID_INET.
+     * @hide
+     */
+    public static final int INET_GID = 3003;
+
+    /** {@hide} */
+    public static final int NOBODY_UID = 9999;
+
+    /**
+     * Defines the start of a range of UIDs (and GIDs), going from this
+     * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
+     * to applications.
+     */
+    public static final int FIRST_APPLICATION_UID = 10000;
+
+    /**
+     * Last of application-specific UIDs starting at
+     * {@link #FIRST_APPLICATION_UID}.
+     */
+    public static final int LAST_APPLICATION_UID = 19999;
+
+    /**
+     * Defines the start of a range of UIDs going from this number to
+     * {@link #LAST_SDK_SANDBOX_UID} that are reserved for assigning to
+     * sdk sandbox processes. There is a 1-1 mapping between a sdk sandbox
+     * process UID and the app that it belongs to, which can be computed by
+     * subtracting (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID) from the
+     * uid of a sdk sandbox process.
+     *
+     * Note that there are no GIDs associated with these processes; storage
+     * attribution for them will be done using project IDs.
+     * @hide
+     */
+    public static final int FIRST_SDK_SANDBOX_UID = 20000;
+
+    /**
+     * Last UID that is used for sdk sandbox processes.
+     * @hide
+     */
+    public static final int LAST_SDK_SANDBOX_UID = 29999;
+
+    /**
+     * First uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    @TestApi
+    public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
+
+    /**
+     * Number of UIDs we allocate per application zygote
+     * @hide
+     */
+    @TestApi
+    public static final int NUM_UIDS_PER_APP_ZYGOTE = 100;
+
+    /**
+     * Last uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    @TestApi
+    public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;
+
+    /**
+     * First uid used for fully isolated sandboxed processes (with no permissions of their own)
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
+    public static final int FIRST_ISOLATED_UID = 99000;
+
+    /**
+     * Last uid used for fully isolated sandboxed processes (with no permissions of their own)
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
+    public static final int LAST_ISOLATED_UID = 99999;
+
+    /**
+     * Defines the gid shared by all applications running under the same profile.
+     * @hide
+     */
+    public static final int SHARED_USER_GID = 9997;
+
+    /**
+     * First gid for applications to share resources. Used when forward-locking
+     * is enabled but all UserHandles need to be able to read the resources.
+     * @hide
+     */
+    public static final int FIRST_SHARED_APPLICATION_GID = 50000;
+
+    /**
+     * Last gid for applications to share resources. Used when forward-locking
+     * is enabled but all UserHandles need to be able to read the resources.
+     * @hide
+     */
+    public static final int LAST_SHARED_APPLICATION_GID = 59999;
+
+    /** {@hide} */
+    public static final int FIRST_APPLICATION_CACHE_GID = 20000;
+    /** {@hide} */
+    public static final int LAST_APPLICATION_CACHE_GID = 29999;
+
+    /**
+     * An invalid PID value.
+     */
+    public static final int INVALID_PID = -1;
+
+    /**
+     * Standard priority of application threads.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_DEFAULT = 0;
+
+    /*
+     * ***************************************
+     * ** Keep in sync with utils/threads.h **
+     * ***************************************
+     */
+
+    /**
+     * Lowest available thread priority.  Only for those who really, really
+     * don't want to run if anything else is happening.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_LOWEST = 19;
+
+    /**
+     * Standard priority background threads.  This gives your thread a slightly
+     * lower than normal priority, so that it will have less chance of impacting
+     * the responsiveness of the user interface.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_BACKGROUND = 10;
+
+    /**
+     * Standard priority of threads that are currently running a user interface
+     * that the user is interacting with.  Applications can not normally
+     * change to this priority; the system will automatically adjust your
+     * application threads as the user moves through the UI.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_FOREGROUND = -2;
+
+    /**
+     * Standard priority of system display threads, involved in updating
+     * the user interface.  Applications can not
+     * normally change to this priority.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_DISPLAY = -4;
+
+    /**
+     * Standard priority of the most important display threads, for compositing
+     * the screen and retrieving input events.  Applications can not normally
+     * change to this priority.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
+
+    /**
+     * Standard priority of video threads.  Applications can not normally
+     * change to this priority.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_VIDEO = -10;
+
+    /**
+     * Priority we boost main thread and RT of top app to.
+     * @hide
+     */
+    public static final int THREAD_PRIORITY_TOP_APP_BOOST = -10;
+
+    /**
+     * Standard priority of audio threads.  Applications can not normally
+     * change to this priority.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_AUDIO = -16;
+
+    /**
+     * Standard priority of the most important audio threads.
+     * Applications can not normally change to this priority.
+     * Use with {@link #setThreadPriority(int)} and
+     * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+     * {@link java.lang.Thread} class.
+     */
+    public static final int THREAD_PRIORITY_URGENT_AUDIO = -19;
+
+    /**
+     * Minimum increment to make a priority more favorable.
+     */
+    public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1;
+
+    /**
+     * Minimum increment to make a priority less favorable.
+     */
+    public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;
+
+    /**
+     * Default scheduling policy
+     * @hide
+     */
+    public static final int SCHED_OTHER = 0;
+
+    /**
+     * First-In First-Out scheduling policy
+     * @hide
+     */
+    public static final int SCHED_FIFO = 1;
+
+    /**
+     * Round-Robin scheduling policy
+     * @hide
+     */
+    public static final int SCHED_RR = 2;
+
+    /**
+     * Batch scheduling policy
+     * @hide
+     */
+    public static final int SCHED_BATCH = 3;
+
+    /**
+     * Idle scheduling policy
+     * @hide
+     */
+    public static final int SCHED_IDLE = 5;
+
+    /**
+     * Reset scheduler choice on fork.
+     * @hide
+     */
+    public static final int SCHED_RESET_ON_FORK = 0x40000000;
+
+    // Keep in sync with SP_* constants of enum type SchedPolicy
+    // declared in system/core/include/cutils/sched_policy.h,
+    // except THREAD_GROUP_DEFAULT does not correspond to any SP_* value.
+
+    /**
+     * Default thread group -
+     * has meaning with setProcessGroup() only, cannot be used with setThreadGroup().
+     * When used with setProcessGroup(), the group of each thread in the process
+     * is conditionally changed based on that thread's current priority, as follows:
+     * threads with priority numerically less than THREAD_PRIORITY_BACKGROUND
+     * are moved to foreground thread group.  All other threads are left unchanged.
+     * @hide
+     */
+    public static final int THREAD_GROUP_DEFAULT = -1;
+
+    /**
+     * Background thread group - All threads in
+     * this group are scheduled with a reduced share of the CPU.
+     * Value is same as constant SP_BACKGROUND of enum SchedPolicy.
+     * @hide
+     */
+    public static final int THREAD_GROUP_BACKGROUND = 0;
+
+    /**
+     * Foreground thread group - All threads in
+     * this group are scheduled with a normal share of the CPU.
+     * Value is same as constant SP_FOREGROUND of enum SchedPolicy.
+     * Not used at this level.
+     * @hide
+     **/
+    private static final int THREAD_GROUP_FOREGROUND = 1;
+
+    /**
+     * System thread group.
+     * @hide
+     **/
+    public static final int THREAD_GROUP_SYSTEM = 2;
+
+    /**
+     * Application audio thread group.
+     * @hide
+     **/
+    public static final int THREAD_GROUP_AUDIO_APP = 3;
+
+    /**
+     * System audio thread group.
+     * @hide
+     **/
+    public static final int THREAD_GROUP_AUDIO_SYS = 4;
+
+    /**
+     * Thread group for top foreground app.
+     * @hide
+     **/
+    public static final int THREAD_GROUP_TOP_APP = 5;
+
+    /**
+     * Thread group for RT app.
+     * @hide
+     **/
+    public static final int THREAD_GROUP_RT_APP = 6;
+
+    /**
+     * Thread group for bound foreground services that should
+     * have additional CPU restrictions during screen off
+     * @hide
+     **/
+    public static final int THREAD_GROUP_RESTRICTED = 7;
+
+    public static final int SIGNAL_QUIT = 3;
+    public static final int SIGNAL_KILL = 9;
+    public static final int SIGNAL_USR1 = 10;
+
+    /**
+     * When the process started and ActivityThread.handleBindApplication() was executed.
+     */
+    private static long sStartElapsedRealtime;
+
+    /**
+     * When the process started and ActivityThread.handleBindApplication() was executed.
+     */
+    private static long sStartUptimeMillis;
+
+    /**
+     * When the activity manager was about to ask zygote to fork.
+     */
+    private static long sStartRequestedElapsedRealtime;
+
+    /**
+     * When the activity manager was about to ask zygote to fork.
+     */
+    private static long sStartRequestedUptimeMillis;
+
+    private static final int PIDFD_UNKNOWN = 0;
+    private static final int PIDFD_SUPPORTED = 1;
+    private static final int PIDFD_UNSUPPORTED = 2;
+
+    /**
+     * Whether or not the underlying OS supports pidfd
+     */
+    private static int sPidFdSupported = PIDFD_UNKNOWN;
+
+    /**
+     * Value used to indicate that there is no special information about an application launch.  App
+     * launches with this policy will occur through the primary or secondary Zygote with no special
+     * treatment.
+     *
+     * @hide
+     */
+    public static final int ZYGOTE_POLICY_FLAG_EMPTY = 0;
+
+    /**
+     * Flag used to indicate that an application launch is user-visible and latency sensitive.  Any
+     * launch with this policy will use a Unspecialized App Process Pool if the target Zygote
+     * supports it.
+     *
+     * @hide
+     */
+    public static final int ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE = 1 << 0;
+
+    /**
+     * Flag used to indicate that the launch is one in a series of app launches that will be
+     * performed in quick succession.  For future use.
+     *
+     * @hide
+     */
+    public static final int ZYGOTE_POLICY_FLAG_BATCH_LAUNCH = 1 << 1;
+
+    /**
+     * Flag used to indicate that the current launch event is for a system process.  All system
+     * processes are equally important, so none of them should be prioritized over the others.
+     *
+     * @hide
+     */
+    public static final int ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS = 1 << 2;
+
+    /**
+     * State associated with the zygote process.
+     * @hide
+     */
+    public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();
+
+
+    /**
+     * The process name set via {@link #setArgV0(String)}.
+     */
+    private static String sArgV0;
+
+    /**
+     * Start a new process.
+     *
+     * <p>If processes are enabled, a new process is created and the
+     * static main() function of a <var>processClass</var> is executed there.
+     * The process will continue running after this function returns.
+     *
+     * <p>If processes are not enabled, a new thread in the caller's
+     * process is created and main() of <var>processClass</var> called there.
+     *
+     * <p>The niceName parameter, if not an empty string, is a custom name to
+     * give to the process instead of using processClass.  This allows you to
+     * make easily identifyable processes even if you are using the same base
+     * <var>processClass</var> to start them.
+     *
+     * 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
+     * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
+     *
+     * @param processClass The class to use as the process's main entry
+     *                     point.
+     * @param niceName A more readable name to use for the process.
+     * @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 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.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param appDataDir null-ok the data directory of the app.
+     * @param invokeWith null-ok the command to invoke with.
+     * @param packageName null-ok the name of the package this process belongs to.
+     * @param zygotePolicyFlags Flags used to determine how to launch the application
+     * @param isTopApp whether the process starts for high priority application.
+     * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
+     *                             started.
+     * @param pkgDataInfoMap Map from related package names to private data directory
+     *                       volume UUID and inode number.
+     * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
+     * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
+     * @param zygoteArgs Additional arguments to supply to the zygote process.
+     * @return An object that describes the result of the attempt to start the process.
+     * @throws RuntimeException on fatal start failure
+     *
+     * {@hide}
+     */
+    public static ProcessStartResult start(@NonNull final String processClass,
+                                           @Nullable final String niceName,
+                                           int uid, int gid, @Nullable int[] gids,
+                                           int runtimeFlags,
+                                           int mountExternal,
+                                           int targetSdkVersion,
+                                           @Nullable String seInfo,
+                                           @NonNull String abi,
+                                           @Nullable String instructionSet,
+                                           @Nullable String appDataDir,
+                                           @Nullable String invokeWith,
+                                           @Nullable String packageName,
+                                           int zygotePolicyFlags,
+                                           boolean isTopApp,
+                                           @Nullable long[] disabledCompatChanges,
+                                           @Nullable Map<String, Pair<String, Long>>
+                                                   pkgDataInfoMap,
+                                           @Nullable Map<String, Pair<String, Long>>
+                                                   whitelistedDataInfoMap,
+                                           boolean bindMountAppsData,
+                                           boolean bindMountAppStorageDirs,
+                                           @Nullable String[] zygoteArgs) {
+        return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
+                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
+                    abi, instructionSet, appDataDir, invokeWith, packageName,
+                    zygotePolicyFlags, isTopApp, disabledCompatChanges,
+                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+                    bindMountAppStorageDirs, zygoteArgs);
+    }
+
+    /** @hide */
+    public static ProcessStartResult startWebView(@NonNull final String processClass,
+                                                  @Nullable final String niceName,
+                                                  int uid, int gid, @Nullable int[] gids,
+                                                  int runtimeFlags,
+                                                  int mountExternal,
+                                                  int targetSdkVersion,
+                                                  @Nullable String seInfo,
+                                                  @NonNull String abi,
+                                                  @Nullable String instructionSet,
+                                                  @Nullable String appDataDir,
+                                                  @Nullable String invokeWith,
+                                                  @Nullable String packageName,
+                                                  @Nullable long[] disabledCompatChanges,
+                                                  @Nullable String[] zygoteArgs) {
+        // Webview zygote can't access app private data files, so doesn't need to know its data
+        // info.
+        return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
+                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
+                    abi, instructionSet, appDataDir, invokeWith, packageName,
+                    /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
+                disabledCompatChanges, /* pkgDataInfoMap */ null,
+                /* whitelistedDataInfoMap */ null, false, false, zygoteArgs);
+    }
+
+    /**
+     * Returns elapsed milliseconds of the time this process has run.
+     * @return  Returns the number of milliseconds this process has return.
+     */
+    public static final native long getElapsedCpuTime();
+
+    /**
+     * Return the {@link SystemClock#elapsedRealtime()} at which this process was started,
+     * but before any of the application code was executed.
+     */
+    @ElapsedRealtimeLong
+    public static long getStartElapsedRealtime() {
+        return sStartElapsedRealtime;
+    }
+
+    /**
+     * Return the {@link SystemClock#uptimeMillis()} at which this process was started,
+     * but before any of the application code was executed.
+     */
+    @UptimeMillisLong
+    public static long getStartUptimeMillis() {
+        return sStartUptimeMillis;
+    }
+
+    /**
+     * Return the {@link SystemClock#elapsedRealtime()} at which the system was about to
+     * start this process. i.e. before a zygote fork.
+     *
+     * <p>More precisely, the system may start app processes before there's a start request,
+     * in order to reduce the process start up latency, in which case this is set when the system
+     * decides to "specialize" the process into a requested app.
+     */
+    @ElapsedRealtimeLong
+    public static long getStartRequestedElapsedRealtime() {
+        return sStartRequestedElapsedRealtime;
+    }
+
+    /**
+     * Return the {@link SystemClock#uptimeMillis()} at which the system was about to
+     * start this process. i.e. before a zygote fork.
+     *
+     * <p>More precisely, the system may start app processes before there's a start request,
+     * in order to reduce the process start up latency, in which case this is set when the system
+     * decides to "specialize" the process into a requested app.
+     */
+    @UptimeMillisLong
+    public static long getStartRequestedUptimeMillis() {
+        return sStartRequestedUptimeMillis;
+    }
+
+    /** @hide */
+    public static final void setStartTimes(long elapsedRealtime, long uptimeMillis,
+            long startRequestedElapsedRealtime, long startRequestedUptime) {
+        sStartElapsedRealtime = elapsedRealtime;
+        sStartUptimeMillis = uptimeMillis;
+        sStartRequestedElapsedRealtime = startRequestedElapsedRealtime;
+        sStartRequestedUptimeMillis = startRequestedUptime;
+    }
+
+    /**
+     * Returns true if the current process is a 64-bit runtime.
+     */
+    public static final boolean is64Bit() {
+        return VMRuntime.getRuntime().is64Bit();
+    }
+
+    /**
+     * Returns the identifier of this process, which can be used with
+     * {@link #killProcess} and {@link #sendSignal}.
+     */
+    public static final int myPid() {
+        return Os.getpid();
+    }
+
+    /**
+     * Returns the identifier of this process' parent.
+     * @hide
+     */
+    @UnsupportedAppUsage(trackingBug = 171962076)
+    public static final int myPpid() {
+        return Os.getppid();
+    }
+
+    /**
+     * Returns the identifier of the calling thread, which be used with
+     * {@link #setThreadPriority(int, int)}.
+     */
+    public static final int myTid() {
+        return Os.gettid();
+    }
+
+    /**
+     * Returns the identifier of this process's uid.  This is the kernel uid
+     * that the process is running under, which is the identity of its
+     * app-specific sandbox.  It is different from {@link #myUserHandle} in that
+     * a uid identifies a specific app sandbox in a specific user.
+     */
+    public static final int myUid() {
+        return Os.getuid();
+    }
+
+    /**
+     * Returns this process's user handle.  This is the
+     * user the process is running under.  It is distinct from
+     * {@link #myUid()} in that a particular user will have multiple
+     * distinct apps running under it each with their own uid.
+     */
+    public static UserHandle myUserHandle() {
+        return UserHandle.of(UserHandle.getUserId(myUid()));
+    }
+
+    /**
+     * Returns whether the given uid belongs to a system core component or not.
+     * @hide
+     */
+    public static boolean isCoreUid(int uid) {
+        return UserHandle.isCore(uid);
+    }
+
+    /**
+     * Returns whether the given uid belongs to an application.
+     * @param uid A kernel uid.
+     * @return Whether the uid corresponds to an application sandbox running in
+     *     a specific user.
+     */
+    public static boolean isApplicationUid(int uid) {
+        return UserHandle.isApp(uid);
+    }
+
+    /**
+     * Returns whether the current process is in an isolated sandbox.
+     */
+    public static final boolean isIsolated() {
+        return isIsolated(myUid());
+    }
+
+    /**
+     * @deprecated Use {@link #isIsolatedUid(int)} instead.
+     * {@hide}
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@link #isIsolatedUid(int)} instead.")
+    public static final boolean isIsolated(int uid) {
+        return isIsolatedUid(uid);
+    }
+
+    /**
+     * Returns whether the process with the given {@code uid} is an isolated sandbox.
+     */
+    public static final boolean isIsolatedUid(int uid) {
+        uid = UserHandle.getAppId(uid);
+        return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID)
+                || (uid >= FIRST_APP_ZYGOTE_ISOLATED_UID && uid <= LAST_APP_ZYGOTE_ISOLATED_UID);
+    }
+
+    /**
+     * Returns whether the provided UID belongs to a SDK sandbox process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @TestApi
+    public static final boolean isSdkSandboxUid(int uid) {
+        uid = UserHandle.getAppId(uid);
+        return (uid >= FIRST_SDK_SANDBOX_UID && uid <= LAST_SDK_SANDBOX_UID);
+    }
+
+    /**
+     *
+     * Returns the app process corresponding to an sdk sandbox process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @TestApi
+    public static final int getAppUidForSdkSandboxUid(int uid) {
+        return uid - (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
+    }
+
+    /**
+     *
+     * Returns the sdk sandbox process corresponding to an app process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @TestApi
+    public static final int toSdkSandboxUid(int uid) {
+        return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
+    }
+
+    /**
+     * Returns whether the current process is a sdk sandbox process.
+     */
+    public static final boolean isSdkSandbox() {
+        return isSdkSandboxUid(myUid());
+    }
+
+    /**
+     * Returns the UID assigned to a particular user name, or -1 if there is
+     * none.  If the given string consists of only numbers, it is converted
+     * directly to a uid.
+     */
+    public static final native int getUidForName(String name);
+
+    /**
+     * Returns the GID assigned to a particular user name, or -1 if there is
+     * none.  If the given string consists of only numbers, it is converted
+     * directly to a gid.
+     */
+    public static final native int getGidForName(String name);
+
+    /**
+     * Returns a uid for a currently running process.
+     * @param pid the process id
+     * @return the uid of the process, or -1 if the process is not running.
+     * @hide pending API council review
+     */
+    @UnsupportedAppUsage
+    public static final int getUidForPid(int pid) {
+        String[] procStatusLabels = { "Uid:" };
+        long[] procStatusValues = new long[1];
+        procStatusValues[0] = -1;
+        Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+        return (int) procStatusValues[0];
+    }
+
+    /**
+     * Returns the parent process id for a currently running process.
+     * @param pid the process id
+     * @return the parent process id of the process, or -1 if the process is not running.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int getParentPid(int pid) {
+        String[] procStatusLabels = { "PPid:" };
+        long[] procStatusValues = new long[1];
+        procStatusValues[0] = -1;
+        Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+        return (int) procStatusValues[0];
+    }
+
+    /**
+     * Returns the thread group leader id for a currently running thread.
+     * @param tid the thread id
+     * @return the thread group leader id of the thread, or -1 if the thread is not running.
+     *         This is same as what getpid(2) would return if called by tid.
+     * @hide
+     */
+    public static final int getThreadGroupLeader(int tid) {
+        String[] procStatusLabels = { "Tgid:" };
+        long[] procStatusValues = new long[1];
+        procStatusValues[0] = -1;
+        Process.readProcLines("/proc/" + tid + "/status", procStatusLabels, procStatusValues);
+        return (int) procStatusValues[0];
+    }
+
+    /**
+     * Set the priority of a thread, based on Linux priorities.
+     *
+     * @param tid The identifier of the thread/process to change.
+     * @param priority A Linux priority level, from -20 for highest scheduling
+     * priority to 19 for lowest scheduling priority.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * priority.
+     */
+    public static final native void setThreadPriority(int tid, int priority)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to
+     * throw an exception if passed a background-level thread priority.  This is only
+     * effective if the JNI layer is built with GUARD_THREAD_PRIORITY defined to 1.
+     *
+     * @hide
+     */
+    public static final native void setCanSelfBackground(boolean backgroundOk);
+
+    /**
+     * Sets the scheduling group for a thread.
+     * @hide
+     * @param tid The identifier of the thread to change.
+     * @param group The target group for this thread from THREAD_GROUP_*.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * priority.
+     * If the thread is a thread group leader, that is it's gettid() == getpid(),
+     * then the other threads in the same thread group are _not_ affected.
+     *
+     * Does not set cpuset for some historical reason, just calls
+     * libcutils::set_sched_policy().
+     */
+    public static final native void setThreadGroup(int tid, int group)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Sets the scheduling group and the corresponding cpuset group
+     * @hide
+     * @param tid The identifier of the thread to change.
+     * @param group The target group for this thread from THREAD_GROUP_*.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * priority.
+     */
+    public static final native void setThreadGroupAndCpuset(int tid, int group)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Sets the scheduling group for a process and all child threads
+     * @hide
+     * @param pid The identifier of the process to change.
+     * @param group The target group for this process from THREAD_GROUP_*.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * priority.
+     *
+     * group == THREAD_GROUP_DEFAULT means to move all non-background priority
+     * threads to the foreground scheduling group, but to leave background
+     * priority threads alone.  group == THREAD_GROUP_BACKGROUND moves all
+     * threads, regardless of priority, to the background scheduling group.
+     * group == THREAD_GROUP_FOREGROUND is not allowed.
+     *
+     * Always sets cpusets.
+     */
+    @UnsupportedAppUsage
+    public static final native void setProcessGroup(int pid, int group)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Freeze or unfreeze the specified process.
+     *
+     * @param pid Identifier of the process to freeze or unfreeze.
+     * @param uid Identifier of the user the process is running under.
+     * @param frozen Specify whether to free (true) or unfreeze (false).
+     *
+     * @hide
+     */
+    public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
+
+    /**
+     * Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
+     * but aren't removed from the freezer. While in this state, processes can be added or removed
+     * by using setProcessFrozen, but they won't actually be frozen until the freezer is enabled
+     * again. If enable == true the freezer is enabled again, and all processes
+     * in the freezer (including the ones added while the freezer was disabled) are frozen.
+     *
+     * @param enable Specify whether to enable (true) or disable (false) the freezer.
+     *
+     * @hide
+     */
+    public static final native void enableFreezer(boolean enable);
+
+    /**
+     * Return the scheduling group of requested process.
+     *
+     * @hide
+     */
+    public static final native int getProcessGroup(int pid)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     *
+     * Create a new process group in the cgroup uid/pid hierarchy
+     *
+     * @return <0 in case of error
+     *
+     * @hide
+     */
+    public static final native int createProcessGroup(int uid, int pid);
+
+    /**
+     * On some devices, the foreground process may have one or more CPU
+     * cores exclusively reserved for it. This method can be used to
+     * retrieve which cores that are (if any), so the calling process
+     * can then use sched_setaffinity() to lock a thread to these cores.
+     * Note that the calling process must currently be running in the
+     * foreground for this method to return any cores.
+     *
+     * The CPU core(s) exclusively reserved for the foreground process will
+     * stay reserved for as long as the process stays in the foreground.
+     *
+     * As soon as a process leaves the foreground, those CPU cores will
+     * no longer be reserved for it, and will most likely be reserved for
+     * the new foreground process. It's not necessary to change the affinity
+     * of your process when it leaves the foreground (if you had previously
+     * set it to use a reserved core); the OS will automatically take care
+     * of resetting the affinity at that point.
+     *
+     * @return an array of integers, indicating the CPU cores exclusively
+     * reserved for this process. The array will have length zero if no
+     * CPU cores are exclusively reserved for this process at this point
+     * in time.
+     */
+    public static final native int[] getExclusiveCores();
+
+    /**
+     * Set the priority of the calling thread, based on Linux priorities.  See
+     * {@link #setThreadPriority(int, int)} for more information.
+     *
+     * @param priority A Linux priority level, from -20 for highest scheduling
+     * priority to 19 for lowest scheduling priority.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * priority.
+     *
+     * @see #setThreadPriority(int, int)
+     */
+    public static final native void setThreadPriority(int priority)
+            throws IllegalArgumentException, SecurityException;
+
+    /**
+     * Return the current priority of a thread, based on Linux priorities.
+     *
+     * @param tid The identifier of the thread/process. If tid equals zero, the priority of the
+     * calling process/thread will be returned.
+     *
+     * @return Returns the current priority, as a Linux priority level,
+     * from -20 for highest scheduling priority to 19 for lowest scheduling
+     * priority.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist.
+     */
+    public static final native int getThreadPriority(int tid)
+            throws IllegalArgumentException;
+
+    /**
+     * Return the current scheduling policy of a thread, based on Linux.
+     *
+     * @param tid The identifier of the thread/process to get the scheduling policy.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist, or if <var>priority</var> is out of range for the policy.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * scheduling policy or priority.
+     *
+     * {@hide}
+     */
+
+    @TestApi
+    public static final native int getThreadScheduler(int tid)
+            throws IllegalArgumentException;
+
+    /**
+     * Set the scheduling policy and priority of a thread, based on Linux.
+     *
+     * @param tid The identifier of the thread/process to change.
+     * @param policy A Linux scheduling policy such as SCHED_OTHER etc.
+     * @param priority A Linux priority level in a range appropriate for the given policy.
+     *
+     * @throws IllegalArgumentException Throws IllegalArgumentException if
+     * <var>tid</var> does not exist, or if <var>priority</var> is out of range for the policy.
+     * @throws SecurityException Throws SecurityException if your process does
+     * not have permission to modify the given thread, or to use the given
+     * scheduling policy or priority.
+     *
+     * {@hide}
+     */
+
+    public static final native void setThreadScheduler(int tid, int policy, int priority)
+            throws IllegalArgumentException;
+
+    /**
+     * Determine whether the current environment supports multiple processes.
+     *
+     * @return Returns true if the system can run in multiple processes, else
+     * false if everything is running in a single process.
+     *
+     * @deprecated This method always returns true.  Do not use.
+     */
+    @Deprecated
+    public static final boolean supportsProcesses() {
+        return true;
+    }
+
+    /**
+     * Adjust the swappiness level for a process.
+     *
+     * @param pid The process identifier to set.
+     * @param is_increased Whether swappiness should be increased or default.
+     *
+     * @return Returns true if the underlying system supports this
+     *         feature, else false.
+     *
+     * {@hide}
+     */
+    public static final native boolean setSwappiness(int pid, boolean is_increased);
+
+    /**
+     * Change this process's argv[0] parameter.  This can be useful to show
+     * more descriptive information in things like the 'ps' command.
+     *
+     * @param text The new name of this process.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.S, publicAlternatives = "Do not try to "
+            + "change the process name. (If you must, you could use {@code pthread_setname_np(3)}, "
+            + "but this could confuse the system)")
+    public static void setArgV0(@NonNull String text) {
+        sArgV0 = text;
+        setArgV0Native(text);
+    }
+
+    private static native void setArgV0Native(String text);
+
+    /**
+     * Return the name of this process. By default, the process name is the same as the app's
+     * package name, but this can be changed using {@code android:process}.
+     */
+    @NonNull
+    public static String myProcessName() {
+        // Note this could be different from the actual process name if someone changes the
+        // process name using native code (using pthread_setname_np()). But sArgV0
+        // is the name that the system thinks this process has.
+        return sArgV0;
+    }
+
+    /**
+     * Kill the process with the given PID.
+     * Note that, though this API allows us to request to
+     * kill any process based on its PID, the kernel will
+     * still impose standard restrictions on which PIDs you
+     * are actually able to kill.  Typically this means only
+     * the process running the caller's packages/application
+     * and any additional processes created by that app; packages
+     * sharing a common UID will also be able to kill each
+     * other's processes.
+     */
+    public static final void killProcess(int pid) {
+        sendSignal(pid, SIGNAL_KILL);
+    }
+
+    /** @hide */
+    public static final native int setUid(int uid);
+
+    /** @hide */
+    public static final native int setGid(int uid);
+
+    /**
+     * Send a signal to the given process.
+     *
+     * @param pid The pid of the target process.
+     * @param signal The signal to send.
+     */
+    public static final native void sendSignal(int pid, int signal);
+
+    /**
+     * @hide
+     * Private impl for avoiding a log message...  DO NOT USE without doing
+     * your own log, or the Android Illuminati will find you some night and
+     * beat you up.
+     */
+    public static final void killProcessQuiet(int pid) {
+        sendSignalQuiet(pid, SIGNAL_KILL);
+    }
+
+    /**
+     * @hide
+     * Private impl for avoiding a log message...  DO NOT USE without doing
+     * your own log, or the Android Illuminati will find you some night and
+     * beat you up.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final native void sendSignalQuiet(int pid, int signal);
+
+    /**
+     * @return The advertised memory of the system, as the end user would encounter in a retail
+     * display environment. If the advertised memory is not defined, it returns
+     * {@code getTotalMemory()} rounded.
+     *
+     * @hide
+     */
+    public static final long getAdvertisedMem() {
+        String formatSize = MemoryProperties.memory_ddr_size().orElse("0KB");
+        long memSize = FileUtils.parseSize(formatSize);
+
+        if (memSize <= 0) {
+            return FileUtils.roundStorageSize(getTotalMemory());
+        }
+
+        return memSize;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final native long getFreeMemory();
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final native long getTotalMemory();
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final native void readProcLines(String path,
+            String[] reqFields, long[] outSizes);
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final native int[] getPids(String path, int[] lastArray);
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_TERM_MASK = 0xff;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_ZERO_TERM = 0;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_SPACE_TERM = (int)' ';
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_TAB_TERM = (int)'\t';
+    /** @hide */
+    public static final int PROC_NEWLINE_TERM = (int) '\n';
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_COMBINE = 0x100;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_PARENS = 0x200;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_QUOTES = 0x400;
+    /** @hide */
+    public static final int PROC_CHAR = 0x800;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_OUT_STRING = 0x1000;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_OUT_LONG = 0x2000;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int PROC_OUT_FLOAT = 0x4000;
+
+    /**
+     * Read and parse a {@code proc} file in the given format.
+     *
+     * <p>The format is a list of integers, where every integer describes a variable in the file. It
+     * specifies how the variable is syntactically terminated (e.g. {@link Process#PROC_SPACE_TERM},
+     * {@link Process#PROC_TAB_TERM}, {@link Process#PROC_ZERO_TERM}, {@link
+     * Process#PROC_NEWLINE_TERM}).
+     *
+     * <p>If the variable should be parsed and returned to the caller, the termination type should
+     * be binary OR'd with the type of output (e.g. {@link Process#PROC_OUT_STRING}, {@link
+     * Process#PROC_OUT_LONG}, {@link Process#PROC_OUT_FLOAT}.
+     *
+     * <p>If the variable is wrapped in quotation marks it should be binary OR'd with {@link
+     * Process#PROC_QUOTES}. If the variable is wrapped in parentheses it should be binary OR'd with
+     * {@link Process#PROC_PARENS}.
+     *
+     * <p>If the variable is not formatted as a string and should be cast directly from characters
+     * to a long, the {@link Process#PROC_CHAR} integer should be binary OR'd.
+     *
+     * <p>If the terminating character can be repeated, the {@link Process#PROC_COMBINE} integer
+     * should be binary OR'd.
+     *
+     * @param file the path of the {@code proc} file to read
+     * @param format the format of the file
+     * @param outStrings the parsed {@code String}s from the file
+     * @param outLongs the parsed {@code long}s from the file
+     * @param outFloats the parsed {@code float}s from the file
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final native boolean readProcFile(String file, int[] format,
+            String[] outStrings, long[] outLongs, float[] outFloats);
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final native boolean parseProcLine(byte[] buffer, int startIndex,
+            int endIndex, int[] format, String[] outStrings, long[] outLongs, float[] outFloats);
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final native int[] getPidsForCommands(String[] cmds);
+
+    /**
+     * Gets the total Pss value for a given process, in bytes.
+     *
+     * @param pid the process to the Pss for
+     * @return the total Pss value for the given process in bytes,
+     *  or -1 if the value cannot be determined
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final native long getPss(int pid);
+
+    /** @hide */
+    public static final native long[] getRss(int pid);
+
+    /**
+     * Specifies the outcome of having started a process.
+     * @hide
+     */
+    public static final class ProcessStartResult {
+        /**
+         * The PID of the newly started process.
+         * Always >= 0.  (If the start failed, an exception will have been thrown instead.)
+         */
+        public int pid;
+
+        /**
+         * True if the process was started with a wrapper attached.
+         */
+        public boolean usingWrapper;
+    }
+
+    /**
+     * Kill all processes in a process group started for the given
+     * pid.
+     * @hide
+     */
+    public static final native int killProcessGroup(int uid, int pid);
+
+    /**
+     * Send a signal to all processes in a group under the given PID, but do not wait for the
+     * processes to be fully cleaned up, or for the cgroup to be removed before returning.
+     * Callers should also ensure that killProcessGroup is called later to ensure the cgroup is
+     * fully removed, otherwise system resources may leak.
+     * @hide
+     */
+    public static final native int sendSignalToProcessGroup(int uid, int pid, int signal);
+
+    /**
+      * Freeze the cgroup for the given UID.
+      * This cgroup may contain child cgroups which will also be frozen. If this cgroup or its
+      * children contain processes with Binder interfaces, those interfaces should be frozen before
+      * the cgroup to avoid blocking synchronous callers indefinitely.
+      *
+      * @param uid The UID to be frozen
+      * @param freeze true = freeze; false = unfreeze
+      * @hide
+      */
+    public static final native void freezeCgroupUid(int uid, boolean freeze);
+
+    /**
+     * Remove all process groups.  Expected to be called when ActivityManager
+     * is restarted.
+     * @hide
+     */
+    public static final native void removeAllProcessGroups();
+
+    /**
+     * Check to see if a thread belongs to a given process. This may require
+     * more permissions than apps generally have.
+     * @return true if this thread belongs to a process
+     * @hide
+     */
+    public static final boolean isThreadInProcess(int tid, int pid) {
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            if (Os.access("/proc/" + tid + "/task/" + pid, OsConstants.F_OK)) {
+                return true;
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            return false;
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+
+    }
+
+    /**
+     * Wait for the death of the given process.
+     *
+     * @param pid The process ID to be waited on
+     * @param timeout The maximum time to wait in milliseconds, or -1 to wait forever
+     * @hide
+     */
+    public static void waitForProcessDeath(int pid, int timeout)
+            throws InterruptedException, TimeoutException {
+        boolean fallback = supportsPidFd();
+        if (!fallback) {
+            FileDescriptor pidfd = null;
+            try {
+                final int fd = nativePidFdOpen(pid, 0);
+                if (fd >= 0) {
+                    pidfd = new FileDescriptor();
+                    pidfd.setInt$(fd);
+                } else {
+                    fallback = true;
+                }
+                if (pidfd != null) {
+                    StructPollfd[] fds = new StructPollfd[] {
+                        new StructPollfd()
+                    };
+                    fds[0].fd = pidfd;
+                    fds[0].events = (short) OsConstants.POLLIN;
+                    fds[0].revents = 0;
+                    fds[0].userData = null;
+                    int res = Os.poll(fds, timeout);
+                    if (res > 0) {
+                        return;
+                    } else if (res == 0) {
+                        throw new TimeoutException();
+                    } else {
+                        // We should get an ErrnoException now
+                    }
+                }
+            } catch (ErrnoException e) {
+                if (e.errno == OsConstants.EINTR) {
+                    throw new InterruptedException();
+                }
+                fallback = true;
+            } finally {
+                if (pidfd != null) {
+                    IoUtils.closeQuietly(pidfd);
+                }
+            }
+        }
+        if (fallback) {
+            boolean infinity = timeout < 0;
+            long now = System.currentTimeMillis();
+            final long end = now + timeout;
+            while (infinity || now < end) {
+                try {
+                    Os.kill(pid, 0);
+                } catch (ErrnoException e) {
+                    if (e.errno == OsConstants.ESRCH) {
+                        return;
+                    }
+                }
+                Thread.sleep(1);
+                now = System.currentTimeMillis();
+            }
+        }
+        throw new TimeoutException();
+    }
+
+    /**
+     * Determine whether the system supports pidfd APIs
+     *
+     * @return Returns true if the system supports pidfd APIs
+     * @hide
+     */
+    public static boolean supportsPidFd() {
+        if (sPidFdSupported == PIDFD_UNKNOWN) {
+            int fd = -1;
+            try {
+                fd = nativePidFdOpen(myPid(), 0);
+                sPidFdSupported = PIDFD_SUPPORTED;
+            } catch (ErrnoException e) {
+                sPidFdSupported = e.errno != OsConstants.ENOSYS
+                        ? PIDFD_SUPPORTED : PIDFD_UNSUPPORTED;
+            } finally {
+                if (fd >= 0) {
+                    final FileDescriptor f = new FileDescriptor();
+                    f.setInt$(fd);
+                    IoUtils.closeQuietly(f);
+                }
+            }
+        }
+        return sPidFdSupported == PIDFD_SUPPORTED;
+    }
+
+    /**
+     * Open process file descriptor for given pid.
+     *
+     * @param pid The process ID to open for
+     * @param flags Reserved, unused now, must be 0
+     * @return The process file descriptor for given pid
+     * @throws IOException if it can't be opened
+     *
+     * @hide
+     */
+    public static @Nullable FileDescriptor openPidFd(int pid, int flags) throws IOException {
+        if (!supportsPidFd()) {
+            return null;
+        }
+        if (flags != 0) {
+            throw new IllegalArgumentException();
+        }
+        try {
+            FileDescriptor pidfd = new FileDescriptor();
+            pidfd.setInt$(nativePidFdOpen(pid, flags));
+            return pidfd;
+        } catch (ErrnoException e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
+}
diff --git a/android-34/android/os/ProxyFileDescriptorCallback.java b/android-34/android/os/ProxyFileDescriptorCallback.java
new file mode 100644
index 0000000..9f56802
--- /dev/null
+++ b/android-34/android/os/ProxyFileDescriptorCallback.java
@@ -0,0 +1,96 @@
+/*
+ * 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.os;
+
+import android.system.ErrnoException;
+import android.system.OsConstants;
+
+/**
+ * Callback that handles file system requests from ProxyFileDescriptor.
+ *
+ * All callback methods except for onRelease should throw {@link android.system.ErrnoException}
+ * with proper errno on errors. See
+ * <a href="http://man7.org/linux/man-pages/man3/errno.3.html">errno(3)</a> and
+ * {@link android.system.OsConstants}.
+ *
+ * Typical errnos are
+ *
+ * <ul>
+ * <li>{@link android.system.OsConstants#EIO} for general I/O issues
+ * <li>{@link android.system.OsConstants#ENOENT} when the file is not found
+ * <li>{@link android.system.OsConstants#EBADF} if the file doesn't allow read/write operations
+ *     based on how it was opened.  (For example, trying to write a file that was opened read-only.)
+ * <li>{@link android.system.OsConstants#ENOSPC} if you cannot handle a write operation to
+ *     space/quota limitations.
+ * </ul>
+ * @see android.os.storage.StorageManager#openProxyFileDescriptor(int, ProxyFileDescriptorCallback,
+ *     Handler)
+ */
+public abstract class ProxyFileDescriptorCallback {
+    /**
+     * Returns size of bytes provided by the file descriptor.
+     * @return Size of bytes.
+     * @throws ErrnoException ErrnoException containing E constants in OsConstants.
+     */
+    public long onGetSize() throws ErrnoException {
+        throw new ErrnoException("onGetSize", OsConstants.EBADF);
+    }
+
+    /**
+     * Provides bytes read from file descriptor.
+     * It needs to return exact requested size of bytes unless it reaches file end.
+     * @param offset Offset in bytes from the file head specifying where to read bytes. If a seek
+     *     operation is conducted on the file descriptor, then a read operation is requested, the
+     *     offset refrects the proper position of requested bytes.
+     * @param size Size for read bytes.
+     * @param data Byte array to store read bytes.
+     * @return Size of bytes returned by the function.
+     * @throws ErrnoException ErrnoException containing E constants in OsConstants.
+     */
+    public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+        throw new ErrnoException("onRead", OsConstants.EBADF);
+    }
+
+    /**
+     * Handles bytes written to file descriptor.
+     * @param offset Offset in bytes from the file head specifying where to write bytes. If a seek
+     *     operation is conducted on the file descriptor, then a write operation is requested, the
+     *     offset refrects the proper position of requested bytes.
+     * @param size Size for write bytes.
+     * @param data Byte array to be written to somewhere.
+     * @return Size of bytes processed by the function.
+     * @throws ErrnoException ErrnoException containing E constants in OsConstants.
+     */
+    public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+        throw new ErrnoException("onWrite", OsConstants.EBADF);
+    }
+
+    /**
+     * Ensures all the written data are stored in permanent storage device.
+     * For example, if it has data stored in on memory cache, it needs to flush data to storage
+     * device.
+     * @throws ErrnoException ErrnoException containing E constants in OsConstants.
+     */
+    public void onFsync() throws ErrnoException {
+        throw new ErrnoException("onFsync", OsConstants.EINVAL);
+    }
+
+    /**
+     * Invoked after the file is closed.
+     */
+    abstract public void onRelease();
+}
diff --git a/android-34/android/os/PssPerfTest.java b/android-34/android/os/PssPerfTest.java
new file mode 100644
index 0000000..2cc294f
--- /dev/null
+++ b/android-34/android/os/PssPerfTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PssPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testPss() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Debug.getPss();
+        }
+    }
+}
diff --git a/android-34/android/os/RecoverySystem.java b/android-34/android/os/RecoverySystem.java
new file mode 100644
index 0000000..a3b836a
--- /dev/null
+++ b/android-34/android/os/RecoverySystem.java
@@ -0,0 +1,1528 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
+import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.euicc.EuiccManager;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.Display;
+
+import libcore.io.Streams;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+
+/**
+ * RecoverySystem contains methods for interacting with the Android
+ * recovery system (the separate partition that can be used to install
+ * system updates, wipe user data, etc.)
+ */
+@SystemService(Context.RECOVERY_SERVICE)
+public class RecoverySystem {
+    private static final String TAG = "RecoverySystem";
+
+    /**
+     * Default location of zip file containing public keys (X509
+     * certs) authorized to sign OTA updates.
+     */
+    private static final File DEFAULT_KEYSTORE =
+        new File("/system/etc/security/otacerts.zip");
+
+    /** Send progress to listeners no more often than this (in ms). */
+    private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;
+
+    private static final long DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 30000L; // 30 s
+    private static final long MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 5000L; // 5 s
+    private static final long MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 60000L; // 60 s
+
+    private static final long DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
+            45000L; // 45 s
+    private static final long MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 15000L; // 15 s
+    private static final long MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 90000L; // 90 s
+
+    /** Used to communicate with recovery.  See bootable/recovery/recovery.cpp. */
+    private static final File RECOVERY_DIR = new File("/cache/recovery");
+    private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
+    private static final String LAST_INSTALL_PATH = "last_install";
+    private static final String LAST_PREFIX = "last_";
+    private static final String ACTION_EUICC_FACTORY_RESET =
+            "com.android.internal.action.EUICC_FACTORY_RESET";
+    private static final String ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS =
+            "com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS";
+
+    /**
+     * Used in {@link #wipeEuiccData} & {@link #removeEuiccInvisibleSubs} as package name of
+     * callback intent.
+     */
+    private static final String PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK = "android";
+
+    /**
+     * The recovery image uses this file to identify the location (i.e. blocks)
+     * of an OTA package on the /data partition. The block map file is
+     * generated by uncrypt.
+     *
+     * @hide
+     */
+    public static final File BLOCK_MAP_FILE = new File(RECOVERY_DIR, "block.map");
+
+    /**
+     * UNCRYPT_PACKAGE_FILE stores the filename to be uncrypt'd, which will be
+     * read by uncrypt.
+     *
+     * @hide
+     */
+    public static final File UNCRYPT_PACKAGE_FILE = new File(RECOVERY_DIR, "uncrypt_file");
+
+    /**
+     * UNCRYPT_STATUS_FILE stores the time cost (and error code in the case of a failure)
+     * of uncrypt.
+     *
+     * @hide
+     */
+    public static final File UNCRYPT_STATUS_FILE = new File(RECOVERY_DIR, "uncrypt_status");
+
+    // Length limits for reading files.
+    private static final int LOG_FILE_MAX_LENGTH = 64 * 1024;
+
+    // Prevent concurrent execution of requests.
+    private static final Object sRequestLock = new Object();
+
+    private final IRecoverySystem mService;
+
+    /**
+     * The error codes for reboots initiated by resume on reboot clients.
+     *  @hide
+     */
+    @IntDef(prefix = { "RESUME_ON_REBOOT_REBOOT_ERROR_" }, value = {
+            RESUME_ON_REBOOT_REBOOT_ERROR_NONE,
+            RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED,
+            RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME,
+            RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
+            RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
+            RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE})
+    public @interface ResumeOnRebootRebootErrorCode {}
+
+    /**
+     * The preparation of resume on reboot succeeds.
+     *
+     * <p> Don't expose it because a successful reboot should just reboot the device.
+     *  @hide
+     */
+    public static final int RESUME_ON_REBOOT_REBOOT_ERROR_NONE = 0;
+
+    /**
+     * The resume on reboot fails due to an unknown reason.
+     *  @hide
+     */
+    @SystemApi
+    public static final int RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED = 1000;
+
+    /**
+     * The resume on reboot fails because the package name of the client is invalid, e.g. null
+     * packageName, name contains invalid characters, etc.
+     *  @hide
+     */
+    @SystemApi
+    public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000;
+
+    /**
+     * The resume on reboot fails because the Lock Screen Knowledge Factor hasn't been captured.
+     * This error is also reported if the client attempts to reboot without preparing RoR.
+     *  @hide
+     */
+    @SystemApi
+    public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000;
+
+    /**
+     * The resume on reboot fails because the client expects a different boot slot for the next boot
+     * on A/B devices.
+     *  @hide
+     */
+    @SystemApi
+    public static final int RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH = 4000;
+
+    /**
+     * The resume on reboot fails because the resume on reboot provider, e.g. HAL / server based,
+     * fails to arm/store the escrow key.
+     *  @hide
+     */
+    @SystemApi
+    public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000;
+
+    /**
+     * Interface definition for a callback to be invoked regularly as
+     * verification proceeds.
+     */
+    public interface ProgressListener {
+        /**
+         * Called periodically as the verification progresses.
+         *
+         * @param progress  the approximate percentage of the
+         *        verification that has been completed, ranging from 0
+         *        to 100 (inclusive).
+         */
+        public void onProgress(int progress);
+    }
+
+    /** @return the set of certs that can be used to sign an OTA package. */
+    private static HashSet<X509Certificate> getTrustedCerts(File keystore)
+        throws IOException, GeneralSecurityException {
+        HashSet<X509Certificate> trusted = new HashSet<X509Certificate>();
+        if (keystore == null) {
+            keystore = DEFAULT_KEYSTORE;
+        }
+        ZipFile zip = new ZipFile(keystore);
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            Enumeration<? extends ZipEntry> entries = zip.entries();
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = entries.nextElement();
+                InputStream is = zip.getInputStream(entry);
+                try {
+                    trusted.add((X509Certificate) cf.generateCertificate(is));
+                } finally {
+                    is.close();
+                }
+            }
+        } finally {
+            zip.close();
+        }
+        return trusted;
+    }
+
+    /**
+     * Verify the cryptographic signature of a system update package
+     * before installing it.  Note that the package is also verified
+     * separately by the installer once the device is rebooted into
+     * the recovery system.  This function will return only if the
+     * package was successfully verified; otherwise it will throw an
+     * exception.
+     *
+     * Verification of a package can take significant time, so this
+     * function should not be called from a UI thread.  Interrupting
+     * the thread while this function is in progress will result in a
+     * SecurityException being thrown (and the thread's interrupt flag
+     * will be cleared).
+     *
+     * @param packageFile  the package to be verified
+     * @param listener     an object to receive periodic progress
+     * updates as verification proceeds.  May be null.
+     * @param deviceCertsZipFile  the zip file of certificates whose
+     * public keys we will accept.  Verification succeeds if the
+     * package is signed by the private key corresponding to any
+     * public key in this file.  May be null to use the system default
+     * file (currently "/system/etc/security/otacerts.zip").
+     *
+     * @throws IOException if there were any errors reading the
+     * package or certs files.
+     * @throws GeneralSecurityException if verification failed
+     */
+    public static void verifyPackage(File packageFile,
+                                     ProgressListener listener,
+                                     File deviceCertsZipFile)
+        throws IOException, GeneralSecurityException {
+        final long fileLen = packageFile.length();
+
+        final RandomAccessFile raf = new RandomAccessFile(packageFile, "r");
+        try {
+            final long startTimeMillis = System.currentTimeMillis();
+            if (listener != null) {
+                listener.onProgress(0);
+            }
+
+            raf.seek(fileLen - 6);
+            byte[] footer = new byte[6];
+            raf.readFully(footer);
+
+            if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) {
+                throw new SignatureException("no signature in file (no footer)");
+            }
+
+            final int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);
+            final int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);
+
+            byte[] eocd = new byte[commentSize + 22];
+            raf.seek(fileLen - (commentSize + 22));
+            raf.readFully(eocd);
+
+            // Check that we have found the start of the
+            // end-of-central-directory record.
+            if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b ||
+                eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) {
+                throw new SignatureException("no signature in file (bad footer)");
+            }
+
+            for (int i = 4; i < eocd.length-3; ++i) {
+                if (eocd[i  ] == (byte)0x50 && eocd[i+1] == (byte)0x4b &&
+                    eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) {
+                    throw new SignatureException("EOCD marker found after start of EOCD");
+                }
+            }
+
+            // Parse the signature
+            PKCS7 block =
+                new PKCS7(new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));
+
+            // Take the first certificate from the signature (packages
+            // should contain only one).
+            X509Certificate[] certificates = block.getCertificates();
+            if (certificates == null || certificates.length == 0) {
+                throw new SignatureException("signature contains no certificates");
+            }
+            X509Certificate cert = certificates[0];
+            PublicKey signatureKey = cert.getPublicKey();
+
+            SignerInfo[] signerInfos = block.getSignerInfos();
+            if (signerInfos == null || signerInfos.length == 0) {
+                throw new SignatureException("signature contains no signedData");
+            }
+            SignerInfo signerInfo = signerInfos[0];
+
+            // Check that the public key of the certificate contained
+            // in the package equals one of our trusted public keys.
+            boolean verified = false;
+            HashSet<X509Certificate> trusted = getTrustedCerts(
+                deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);
+            for (X509Certificate c : trusted) {
+                if (c.getPublicKey().equals(signatureKey)) {
+                    verified = true;
+                    break;
+                }
+            }
+            if (!verified) {
+                throw new SignatureException("signature doesn't match any trusted key");
+            }
+
+            // The signature cert matches a trusted key.  Now verify that
+            // the digest in the cert matches the actual file data.
+            raf.seek(0);
+            final ProgressListener listenerForInner = listener;
+            SignerInfo verifyResult = block.verify(signerInfo, new InputStream() {
+                // The signature covers all of the OTA package except the
+                // archive comment and its 2-byte length.
+                long toRead = fileLen - commentSize - 2;
+                long soFar = 0;
+
+                int lastPercent = 0;
+                long lastPublishTime = startTimeMillis;
+
+                @Override
+                public int read() throws IOException {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public int read(byte[] b, int off, int len) throws IOException {
+                    if (soFar >= toRead) {
+                        return -1;
+                    }
+                    if (Thread.currentThread().isInterrupted()) {
+                        return -1;
+                    }
+
+                    int size = len;
+                    if (soFar + size > toRead) {
+                        size = (int)(toRead - soFar);
+                    }
+                    int read = raf.read(b, off, size);
+                    soFar += read;
+
+                    if (listenerForInner != null) {
+                        long now = System.currentTimeMillis();
+                        int p = (int)(soFar * 100 / toRead);
+                        if (p > lastPercent &&
+                            now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
+                            lastPercent = p;
+                            lastPublishTime = now;
+                            listenerForInner.onProgress(lastPercent);
+                        }
+                    }
+
+                    return read;
+                }
+            });
+
+            final boolean interrupted = Thread.interrupted();
+            if (listener != null) {
+                listener.onProgress(100);
+            }
+
+            if (interrupted) {
+                throw new SignatureException("verification was interrupted");
+            }
+
+            if (verifyResult == null) {
+                throw new SignatureException("signature digest verification failed");
+            }
+        } finally {
+            raf.close();
+        }
+
+        // Additionally verify the package compatibility.
+        if (!readAndVerifyPackageCompatibilityEntry(packageFile)) {
+            throw new SignatureException("package compatibility verification failed");
+        }
+    }
+
+    /**
+     * Verifies the compatibility entry from an {@link InputStream}.
+     *
+     * @return the verification result.
+     */
+    @UnsupportedAppUsage
+    private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException {
+        ArrayList<String> list = new ArrayList<>();
+        ZipInputStream zis = new ZipInputStream(inputStream);
+        ZipEntry entry;
+        while ((entry = zis.getNextEntry()) != null) {
+            long entrySize = entry.getSize();
+            if (entrySize > Integer.MAX_VALUE || entrySize < 0) {
+                throw new IOException(
+                        "invalid entry size (" + entrySize + ") in the compatibility file");
+            }
+            byte[] bytes = new byte[(int) entrySize];
+            Streams.readFully(zis, bytes);
+            list.add(new String(bytes, UTF_8));
+        }
+        if (list.isEmpty()) {
+            throw new IOException("no entries found in the compatibility file");
+        }
+        return (VintfObject.verify(list.toArray(new String[list.size()])) == 0);
+    }
+
+    /**
+     * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is
+     * a zip file (inside the OTA package zip).
+     *
+     * @return {@code true} if the entry doesn't exist or verification passes.
+     */
+    private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile)
+            throws IOException {
+        try (ZipFile zip = new ZipFile(packageFile)) {
+            ZipEntry entry = zip.getEntry("compatibility.zip");
+            if (entry == null) {
+                return true;
+            }
+            InputStream inputStream = zip.getInputStream(entry);
+            return verifyPackageCompatibility(inputStream);
+        }
+    }
+
+    /**
+     * Verifies the package compatibility info against the current system.
+     *
+     * @param compatibilityFile the {@link File} that contains the package compatibility info.
+     * @throws IOException if there were any errors reading the compatibility file.
+     * @return the compatibility verification result.
+     *
+     * {@hide}
+     */
+    @SystemApi
+    @SuppressLint("RequiresPermission")
+    public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
+        try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
+            return verifyPackageCompatibility(inputStream);
+        }
+    }
+
+    /**
+     * Process a given package with uncrypt. No-op if the package is not on the
+     * /data partition.
+     *
+     * @param Context      the Context to use
+     * @param packageFile  the package to be processed
+     * @param listener     an object to receive periodic progress updates as
+     *                     processing proceeds.  May be null.
+     * @param handler      the Handler upon which the callbacks will be
+     *                     executed.
+     *
+     * @throws IOException if there were any errors processing the package file.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void processPackage(Context context,
+                                      File packageFile,
+                                      final ProgressListener listener,
+                                      final Handler handler)
+            throws IOException {
+        String filename = packageFile.getCanonicalPath();
+        if (!filename.startsWith("/data/")) {
+            return;
+        }
+
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        IRecoverySystemProgressListener progressListener = null;
+        if (listener != null) {
+            final Handler progressHandler;
+            if (handler != null) {
+                progressHandler = handler;
+            } else {
+                progressHandler = new Handler(context.getMainLooper());
+            }
+            progressListener = new IRecoverySystemProgressListener.Stub() {
+                int lastProgress = 0;
+                long lastPublishTime = System.currentTimeMillis();
+
+                @Override
+                public void onProgress(final int progress) {
+                    final long now = System.currentTimeMillis();
+                    progressHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (progress > lastProgress &&
+                                    now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
+                                lastProgress = progress;
+                                lastPublishTime = now;
+                                listener.onProgress(progress);
+                            }
+                        }
+                    });
+                }
+            };
+        }
+
+        if (!rs.uncrypt(filename, progressListener)) {
+            throw new IOException("process package failed");
+        }
+    }
+
+    /**
+     * Process a given package with uncrypt. No-op if the package is not on the
+     * /data partition.
+     *
+     * @param Context      the Context to use
+     * @param packageFile  the package to be processed
+     * @param listener     an object to receive periodic progress updates as
+     *                     processing proceeds.  May be null.
+     *
+     * @throws IOException if there were any errors processing the package file.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void processPackage(Context context,
+                                      File packageFile,
+                                      final ProgressListener listener)
+            throws IOException {
+        processPackage(context, packageFile, listener, null);
+    }
+
+    /**
+     * Reboots the device in order to install the given update
+     * package.
+     * Requires the {@link android.Manifest.permission#REBOOT} permission.
+     *
+     * @param context      the Context to use
+     * @param packageFile  the update package to install.  Must be on
+     * a partition mountable by recovery.  (The set of partitions
+     * known to recovery may vary from device to device.  Generally,
+     * /cache and /data are safe.)
+     *
+     * @throws IOException  if writing the recovery command file
+     * fails, or if the reboot itself fails.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void installPackage(Context context, File packageFile)
+            throws IOException {
+        installPackage(context, packageFile, false);
+    }
+
+    /**
+     * If the package hasn't been processed (i.e. uncrypt'd), set up
+     * UNCRYPT_PACKAGE_FILE and delete BLOCK_MAP_FILE to trigger uncrypt during the
+     * reboot.
+     *
+     * @param context      the Context to use
+     * @param packageFile  the update package to install.  Must be on a
+     * partition mountable by recovery.
+     * @param processed    if the package has been processed (uncrypt'd).
+     *
+     * @throws IOException if writing the recovery command file fails, or if
+     * the reboot itself fails.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void installPackage(Context context, File packageFile, boolean processed)
+            throws IOException {
+        synchronized (sRequestLock) {
+            LOG_FILE.delete();
+            // Must delete the file in case it was created by system server.
+            UNCRYPT_PACKAGE_FILE.delete();
+
+            String filename = packageFile.getCanonicalPath();
+            Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
+
+            // If the package name ends with "_s.zip", it's a security update.
+            boolean securityUpdate = filename.endsWith("_s.zip");
+
+            // If the package is on the /data partition, the package needs to
+            // be processed (i.e. uncrypt'd). The caller specifies if that has
+            // been done in 'processed' parameter.
+            if (filename.startsWith("/data/")) {
+                if (processed) {
+                    if (!BLOCK_MAP_FILE.exists()) {
+                        Log.e(TAG, "Package claimed to have been processed but failed to find "
+                                + "the block map file.");
+                        throw new IOException("Failed to find block map file");
+                    }
+                } else {
+                    FileWriter uncryptFile = new FileWriter(UNCRYPT_PACKAGE_FILE);
+                    try {
+                        uncryptFile.write(filename + "\n");
+                    } finally {
+                        uncryptFile.close();
+                    }
+                    // UNCRYPT_PACKAGE_FILE needs to be readable and writable
+                    // by system server.
+                    if (!UNCRYPT_PACKAGE_FILE.setReadable(true, false)
+                            || !UNCRYPT_PACKAGE_FILE.setWritable(true, false)) {
+                        Log.e(TAG, "Error setting permission for " + UNCRYPT_PACKAGE_FILE);
+                    }
+
+                    BLOCK_MAP_FILE.delete();
+                }
+
+                // If the package is on the /data partition, use the block map
+                // file as the package name instead.
+                filename = "@/cache/recovery/block.map";
+            }
+
+            final String filenameArg = "--update_package=" + filename + "\n";
+            final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n";
+            final String securityArg = "--security\n";
+
+            String command = filenameArg + localeArg;
+            if (securityUpdate) {
+                command += securityArg;
+            }
+
+            RecoverySystem rs = (RecoverySystem) context.getSystemService(
+                    Context.RECOVERY_SERVICE);
+            if (!rs.setupBcb(command)) {
+                throw new IOException("Setup BCB failed");
+            }
+            try {
+                if (!rs.allocateSpaceForUpdate(packageFile)) {
+                    rs.clearBcb();
+                    throw new IOException("Failed to allocate space for update "
+                            + packageFile.getAbsolutePath());
+                }
+            } catch (RemoteException e) {
+                rs.clearBcb();
+                e.rethrowAsRuntimeException();
+            }
+
+            // Having set up the BCB (bootloader control block), go ahead and reboot
+            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+            String reason = PowerManager.REBOOT_RECOVERY_UPDATE;
+
+            // On TV, reboot quiescently if the screen is off
+            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+                DisplayManager dm = context.getSystemService(DisplayManager.class);
+                if (dm.getDisplay(DEFAULT_DISPLAY).getState() != Display.STATE_ON) {
+                    reason += ",quiescent";
+                }
+            }
+            pm.reboot(reason);
+
+            throw new IOException("Reboot failed (no permissions?)");
+        }
+    }
+
+    /**
+     * Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge
+     * Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup
+     * and ready to apply the OTA. <p>
+     *
+     * <p> If the device doesn't setup a lock screen, i.e. by checking
+     * {@link KeyguardManager#isKeyguardSecure()}, this API call will fail and throw an exception.
+     * Callers are expected to use {@link PowerManager#reboot(String)} directly without going
+     * through the RoR flow. <p>
+     *
+     * <p>  This API is expected to handle requests from multiple clients simultaneously, e.g.
+     * from ota and mainline. The behavior of multi-client Resume on Reboot works as follows
+     * <li> Each client should call this function to prepare for Resume on Reboot before calling
+     *      {@link #rebootAndApply(Context, String, boolean)} </li>
+     * <li> One client cannot clear the Resume on Reboot preparation of another client. </li>
+     * <li> If multiple clients have prepared for Resume on Reboot, the subsequent reboot will be
+     *      first come, first served. </li>
+     *
+     * @param context the Context to use.
+     * @param updateToken this parameter is deprecated and won't be used. Callers can supply with
+     *                    an empty string. See details in
+     *                    <a href="http://go/multi-client-ror">http://go/multi-client-ror</a>
+     *                    TODO(xunchang) update the link of document with the public doc.
+     * @param intentSender the intent to call when the update is prepared; may be {@code null}
+     * @throws IOException if there were any errors setting up unattended update
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+            android.Manifest.permission.REBOOT})
+    public static void prepareForUnattendedUpdate(@NonNull Context context,
+            @NonNull String updateToken, @Nullable IntentSender intentSender) throws IOException {
+        if (updateToken == null) {
+            throw new NullPointerException("updateToken == null");
+        }
+
+        KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+        if (keyguardManager == null || !keyguardManager.isDeviceSecure()) {
+            throw new IOException("Failed to request LSKF because the device doesn't have a"
+                    + " lock screen. ");
+        }
+
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        if (!rs.requestLskf(context.getPackageName(), intentSender)) {
+            throw new IOException("preparation for update failed");
+        }
+    }
+
+    /**
+     * Request that any previously requested Lock Screen Knowledge Factor (LSKF) is cleared and
+     * the preparation for unattended update is reset.
+     *
+     * <p> Note that the API won't clear the underlying Resume on Reboot preparation state if
+     * another client has requested. So the reboot call from the other client can still succeed.
+     *
+     * @param context the Context to use.
+     * @throws IOException if there were any errors clearing the unattended update state
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+            android.Manifest.permission.REBOOT})
+    public static void clearPrepareForUnattendedUpdate(@NonNull Context context)
+            throws IOException {
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        if (!rs.clearLskf(context.getPackageName())) {
+            throw new IOException("could not reset unattended update state");
+        }
+    }
+
+    /**
+     * Request that the device reboot and apply the update that has been prepared. This API is
+     * deprecated, and is expected to be used by OTA only on devices running Android 11.
+     *
+     * @param context the Context to use.
+     * @param updateToken this parameter is deprecated and won't be used. See details in
+     *                    <a href="http://go/multi-client-ror">http://go/multi-client-ror</a>
+     *                    TODO(xunchang) update the link of document with the public doc.
+     * @param reason the reboot reason to give to the {@link PowerManager}
+     * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
+     *               unattended reboot or if the {@code updateToken} did not match the previously
+     *               given token
+     * @hide
+     * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken,
+            @NonNull String reason) throws IOException {
+        if (updateToken == null) {
+            throw new NullPointerException("updateToken == null");
+        }
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        // OTA is the sole user, who expects a slot switch.
+        if (rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)
+                != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
+            throw new IOException("system not prepared to apply update");
+        }
+    }
+
+    /**
+     * Query if Resume on Reboot has been prepared for a given caller.
+     *
+     * @param context the Context to use.
+     * @throws IOException if there were any errors connecting to the service or querying the state.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+            android.Manifest.permission.REBOOT})
+    public static boolean isPreparedForUnattendedUpdate(@NonNull Context context)
+            throws IOException {
+        RecoverySystem rs = context.getSystemService(RecoverySystem.class);
+        return rs.isLskfCaptured(context.getPackageName());
+    }
+
+    /**
+     * Request that the device reboot and apply the update that has been prepared.
+     * {@link #prepareForUnattendedUpdate} must be called before for the given client,
+     * otherwise the function call will fail.
+     *
+     * @param context the Context to use.
+     * @param reason the reboot reason to give to the {@link PowerManager}
+     * @param slotSwitch true if the caller expects the slot to be switched on A/B devices.
+     *
+     * @return 0 on success, and a non-zero error code if the reboot couldn't proceed because the
+     *         device wasn't ready for an unattended reboot.
+     * @throws IOException on remote exceptions from the RecoverySystemService
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
+            android.Manifest.permission.REBOOT})
+    public static @ResumeOnRebootRebootErrorCode int rebootAndApply(@NonNull Context context,
+            @NonNull String reason, boolean slotSwitch) throws IOException {
+        RecoverySystem rs = context.getSystemService(RecoverySystem.class);
+        return rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch);
+    }
+
+    /**
+     * Schedule to install the given package on next boot. The caller needs to ensure that the
+     * package must have been processed (uncrypt'd) if needed. It sets up the command in BCB
+     * (bootloader control block), which will be read by the bootloader and the recovery image.
+     *
+     * @param context the Context to use.
+     * @param packageFile the package to be installed.
+     * @throws IOException if there were any errors setting up the BCB.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void scheduleUpdateOnBoot(Context context, File packageFile) throws IOException {
+        String filename = packageFile.getCanonicalPath();
+        boolean securityUpdate = filename.endsWith("_s.zip");
+
+        // If the package is on the /data partition, use the block map file as
+        // the package name instead.
+        if (filename.startsWith("/data/")) {
+            filename = "@/cache/recovery/block.map";
+        }
+
+        final String filenameArg = "--update_package=" + filename + "\n";
+        final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n";
+        final String securityArg = "--security\n";
+
+        String command = filenameArg + localeArg;
+        if (securityUpdate) {
+            command += securityArg;
+        }
+
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        if (!rs.setupBcb(command)) {
+            throw new IOException("schedule update on boot failed");
+        }
+    }
+
+    /**
+     * Cancel any scheduled update by clearing up the BCB (bootloader control
+     * block).
+     *
+     * @param Context      the Context to use.
+     *
+     * @throws IOException if there were any errors clearing up the BCB.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void cancelScheduledUpdate(Context context)
+            throws IOException {
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        if (!rs.clearBcb()) {
+            throw new IOException("cancel scheduled update failed");
+        }
+    }
+
+    /**
+     * Reboots the device and wipes the user data and cache
+     * partitions.  This is sometimes called a "factory reset", which
+     * is something of a misnomer because the system partition is not
+     * restored to its factory state.  Requires the
+     * {@link android.Manifest.permission#REBOOT} permission.
+     *
+     * @param context  the Context to use
+     *
+     * @throws IOException  if writing the recovery command file
+     * fails, or if the reboot itself fails.
+     * @throws SecurityException if the current user is not allowed to wipe data.
+     */
+    public static void rebootWipeUserData(Context context) throws IOException {
+        rebootWipeUserData(context, false /* shutdown */, context.getPackageName(),
+                false /* force */, false /* wipeEuicc */);
+    }
+
+    /** {@hide} */
+    public static void rebootWipeUserData(Context context, String reason) throws IOException {
+        rebootWipeUserData(context, false /* shutdown */, reason, false /* force */,
+                false /* wipeEuicc */);
+    }
+
+    /** {@hide} */
+    public static void rebootWipeUserData(Context context, boolean shutdown)
+            throws IOException {
+        rebootWipeUserData(context, shutdown, context.getPackageName(), false /* force */,
+                false /* wipeEuicc */);
+    }
+
+    /** {@hide} */
+    public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
+            boolean force) throws IOException {
+        rebootWipeUserData(context, shutdown, reason, force, false /* wipeEuicc */);
+    }
+
+    /**
+     * Reboots the device and wipes the user data and cache
+     * partitions.  This is sometimes called a "factory reset", which
+     * is something of a misnomer because the system partition is not
+     * restored to its factory state.  Requires the
+     * {@link android.Manifest.permission#REBOOT} permission.
+     *
+     * @param context   the Context to use
+     * @param shutdown  if true, the device will be powered down after
+     *                  the wipe completes, rather than being rebooted
+     *                  back to the regular system.
+     * @param reason    the reason for the wipe that is visible in the logs
+     * @param force     whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction
+     *                  should be ignored
+     * @param wipeEuicc whether wipe the euicc data
+     *
+     * @throws IOException  if writing the recovery command file
+     * fails, or if the reboot itself fails.
+     * @throws SecurityException if the current user is not allowed to wipe data.
+     *
+     * @hide
+     */
+    public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
+            boolean force, boolean wipeEuicc) throws IOException {
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
+            throw new SecurityException("Wiping data is not allowed for this user.");
+        }
+        final ConditionVariable condition = new ConditionVariable();
+
+        Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
+                android.Manifest.permission.MASTER_CLEAR,
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        condition.open();
+                    }
+                }, null, 0, null, null);
+
+        // Block until the ordered broadcast has completed.
+        condition.block();
+
+        EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
+        if (wipeEuicc) {
+            wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
+        } else {
+            removeEuiccInvisibleSubs(context, euiccManager);
+        }
+
+        String shutdownArg = null;
+        if (shutdown) {
+            shutdownArg = "--shutdown_after";
+        }
+
+        String reasonArg = null;
+        if (!TextUtils.isEmpty(reason)) {
+            String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
+            reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
+        }
+
+        final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
+        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
+    }
+
+    /**
+     * Returns whether wipe Euicc data successfully or not.
+     *
+     * @param packageName the package name of the caller app.
+     *
+     * @hide
+     */
+    public static boolean wipeEuiccData(Context context, final String packageName) {
+        ContentResolver cr = context.getContentResolver();
+        if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
+            // If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles,
+            // as there's nothing to wipe nor retain.
+            Log.d(TAG, "Skipping eUICC wipe/retain as it is not provisioned");
+            return true;
+        }
+
+        EuiccManager euiccManager = (EuiccManager) context.getSystemService(
+                Context.EUICC_SERVICE);
+        if (euiccManager != null && euiccManager.isEnabled()) {
+            CountDownLatch euiccFactoryResetLatch = new CountDownLatch(1);
+            final AtomicBoolean wipingSucceeded = new AtomicBoolean(false);
+
+            BroadcastReceiver euiccWipeFinishReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (ACTION_EUICC_FACTORY_RESET.equals(intent.getAction())) {
+                        if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+                            int detailedCode = intent.getIntExtra(
+                                    EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
+                            Log.e(TAG, "Error wiping euicc data, Detailed code = "
+                                    + detailedCode);
+                        } else {
+                            Log.d(TAG, "Successfully wiped euicc data.");
+                            wipingSucceeded.set(true /* newValue */);
+                        }
+                        euiccFactoryResetLatch.countDown();
+                    }
+                }
+            };
+
+            Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET);
+            intent.setPackage(packageName);
+            PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
+                    context,
+                    0,
+                    intent,
+                    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
+                    UserHandle.SYSTEM);
+            IntentFilter filterConsent = new IntentFilter();
+            filterConsent.addAction(ACTION_EUICC_FACTORY_RESET);
+            HandlerThread euiccHandlerThread = new HandlerThread("euiccWipeFinishReceiverThread");
+            euiccHandlerThread.start();
+            Handler euiccHandler = new Handler(euiccHandlerThread.getLooper());
+            context.getApplicationContext()
+                    .registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler);
+            euiccManager.eraseSubscriptions(callbackIntent);
+            try {
+                long waitingTimeMillis = Settings.Global.getLong(
+                        context.getContentResolver(),
+                        Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
+                        DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS);
+                if (waitingTimeMillis < MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS) {
+                    waitingTimeMillis = MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS;
+                } else if (waitingTimeMillis > MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS) {
+                    waitingTimeMillis = MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS;
+                }
+                if (!euiccFactoryResetLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
+                    Log.e(TAG, "Timeout wiping eUICC data.");
+                    return false;
+                }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                Log.e(TAG, "Wiping eUICC data interrupted", e);
+                return false;
+            } finally {
+                context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
+            }
+            return wipingSucceeded.get();
+        }
+        return false;
+    }
+
+    private static void removeEuiccInvisibleSubs(
+            Context context, EuiccManager euiccManager) {
+        ContentResolver cr = context.getContentResolver();
+        if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
+            // If the eUICC isn't provisioned, there's no need to remove euicc invisible profiles,
+            // as there's nothing to be removed.
+            Log.i(TAG, "Skip removing eUICC invisible profiles as it is not provisioned.");
+            return;
+        } else if (euiccManager == null || !euiccManager.isEnabled()) {
+            Log.i(TAG, "Skip removing eUICC invisible profiles as eUICC manager is not available.");
+            return;
+        }
+        SubscriptionManager subscriptionManager =
+                context.getSystemService(SubscriptionManager.class);
+        List<SubscriptionInfo> availableSubs =
+                subscriptionManager.getAvailableSubscriptionInfoList();
+        if (availableSubs == null || availableSubs.isEmpty()) {
+            Log.i(TAG, "Skip removing eUICC invisible profiles as no available profiles found.");
+            return;
+        }
+        List<SubscriptionInfo> invisibleSubs = new ArrayList<>();
+        for (SubscriptionInfo sub : availableSubs) {
+            if (sub.isEmbedded() && sub.getGroupUuid() != null && sub.isOpportunistic()) {
+                invisibleSubs.add(sub);
+            }
+        }
+        removeEuiccInvisibleSubs(context, invisibleSubs, euiccManager);
+    }
+
+    private static boolean removeEuiccInvisibleSubs(
+            Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager) {
+        if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
+            Log.i(TAG, "There are no eUICC invisible profiles needed to be removed.");
+            return true;
+        }
+        CountDownLatch removeSubsLatch = new CountDownLatch(subscriptionInfos.size());
+        final AtomicInteger removedSubsCount = new AtomicInteger(0);
+
+        BroadcastReceiver removeEuiccSubsReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS.equals(intent.getAction())) {
+                    if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+                        int detailedCode = intent.getIntExtra(
+                                EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
+                        Log.e(TAG, "Error removing euicc opportunistic profile, Detailed code = "
+                                + detailedCode);
+                    } else {
+                        Log.e(TAG, "Successfully remove euicc opportunistic profile.");
+                        removedSubsCount.incrementAndGet();
+                    }
+                    removeSubsLatch.countDown();
+                }
+            }
+        };
+
+        Intent intent = new Intent(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
+        intent.setPackage(PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
+        PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
+                context,
+                0,
+                intent,
+                PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
+                UserHandle.SYSTEM);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
+        HandlerThread euiccHandlerThread =
+                new HandlerThread("euiccRemovingSubsReceiverThread");
+        euiccHandlerThread.start();
+        Handler euiccHandler = new Handler(euiccHandlerThread.getLooper());
+        context.getApplicationContext()
+                .registerReceiver(
+                        removeEuiccSubsReceiver, intentFilter, null, euiccHandler);
+        for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+            Log.i(
+                    TAG,
+                    "Remove invisible subscription " + subscriptionInfo.getSubscriptionId()
+                            + " from card " + subscriptionInfo.getCardId());
+            euiccManager.createForCardId(subscriptionInfo.getCardId())
+                    .deleteSubscription(subscriptionInfo.getSubscriptionId(), callbackIntent);
+        }
+        try {
+            long waitingTimeMillis = Settings.Global.getLong(
+                    context.getContentResolver(),
+                    Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
+                    DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS);
+            if (waitingTimeMillis < MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) {
+                waitingTimeMillis = MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS;
+            } else if (waitingTimeMillis > MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) {
+                waitingTimeMillis = MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS;
+            }
+            if (!removeSubsLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
+                Log.e(TAG, "Timeout removing invisible euicc profiles.");
+                return false;
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            Log.e(TAG, "Removing invisible euicc profiles interrupted", e);
+            return false;
+        } finally {
+            context.getApplicationContext().unregisterReceiver(removeEuiccSubsReceiver);
+            if (euiccHandlerThread != null) {
+                euiccHandlerThread.quit();
+            }
+        }
+        return removedSubsCount.get() == subscriptionInfos.size();
+    }
+
+    /** {@hide} */
+    public static void rebootPromptAndWipeUserData(Context context, String reason)
+            throws IOException {
+        boolean checkpointing = false;
+        boolean needReboot = false;
+        IVold vold = null;
+        try {
+            vold = IVold.Stub.asInterface(ServiceManager.checkService("vold"));
+            if (vold != null) {
+                checkpointing = vold.needsCheckpoint();
+            } else  {
+                Log.w(TAG, "Failed to get vold");
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to check for checkpointing");
+        }
+
+        // If we are running in checkpointing mode, we should not prompt a wipe.
+        // Checkpointing may save us. If it doesn't, we will wind up here again.
+        if (checkpointing) {
+            try {
+                vold.abortChanges("rescueparty", false);
+                Log.i(TAG, "Rescue Party requested wipe. Aborting update");
+            } catch (Exception e) {
+                Log.i(TAG, "Rescue Party requested wipe. Rebooting instead.");
+                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+                pm.reboot("rescueparty");
+            }
+            return;
+        }
+
+        String reasonArg = null;
+        if (!TextUtils.isEmpty(reason)) {
+            reasonArg = "--reason=" + sanitizeArg(reason);
+        }
+
+        final String localeArg = "--locale=" + Locale.getDefault().toString();
+        bootCommand(context, null, "--prompt_and_wipe_data", reasonArg, localeArg);
+    }
+
+    /**
+     * Reboot into the recovery system to wipe the /cache partition.
+     * @throws IOException if something goes wrong.
+     */
+    public static void rebootWipeCache(Context context) throws IOException {
+        rebootWipeCache(context, context.getPackageName());
+    }
+
+    /** {@hide} */
+    public static void rebootWipeCache(Context context, String reason) throws IOException {
+        String reasonArg = null;
+        if (!TextUtils.isEmpty(reason)) {
+            reasonArg = "--reason=" + sanitizeArg(reason);
+        }
+
+        final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
+        bootCommand(context, "--wipe_cache", reasonArg, localeArg);
+    }
+
+    /**
+     * Reboot into recovery and wipe the A/B device.
+     *
+     * @param Context      the Context to use.
+     * @param packageFile  the wipe package to be applied.
+     * @param reason       the reason to wipe.
+     *
+     * @throws IOException if something goes wrong.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.RECOVERY,
+            android.Manifest.permission.REBOOT
+    })
+    public static void rebootWipeAb(Context context, File packageFile, String reason)
+            throws IOException {
+        String reasonArg = null;
+        if (!TextUtils.isEmpty(reason)) {
+            reasonArg = "--reason=" + sanitizeArg(reason);
+        }
+
+        final String filename = packageFile.getCanonicalPath();
+        final String filenameArg = "--wipe_package=" + filename;
+        final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
+        bootCommand(context, "--wipe_ab", filenameArg, reasonArg, localeArg);
+    }
+
+    /**
+     * Reboot into the recovery system with the supplied argument.
+     * @param args to pass to the recovery utility.
+     * @throws IOException if something goes wrong.
+     */
+    private static void bootCommand(Context context, String... args) throws IOException {
+        LOG_FILE.delete();
+
+        StringBuilder command = new StringBuilder();
+        for (String arg : args) {
+            if (!TextUtils.isEmpty(arg)) {
+                command.append(arg);
+                command.append("\n");
+            }
+        }
+
+        // Write the command into BCB (bootloader control block) and boot from
+        // there. Will not return unless failed.
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        rs.rebootRecoveryWithCommand(command.toString());
+
+        throw new IOException("Reboot failed (no permissions?)");
+    }
+
+    /**
+     * Called after booting to process and remove recovery-related files.
+     * @return the log file from recovery, or null if none was found.
+     *
+     * @hide
+     */
+    public static String handleAftermath(Context context) {
+        // Record the tail of the LOG_FILE
+        String log = null;
+        try {
+            log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n");
+        } catch (FileNotFoundException e) {
+            Log.i(TAG, "No recovery log file");
+        } catch (IOException e) {
+            Log.e(TAG, "Error reading recovery log", e);
+        }
+
+
+        // Only remove the OTA package if it's partially processed (uncrypt'd).
+        boolean reservePackage = BLOCK_MAP_FILE.exists();
+        if (!reservePackage && UNCRYPT_PACKAGE_FILE.exists()) {
+            String filename = null;
+            try {
+                filename = FileUtils.readTextFile(UNCRYPT_PACKAGE_FILE, 0, null);
+            } catch (IOException e) {
+                Log.e(TAG, "Error reading uncrypt file", e);
+            }
+
+            // Remove the OTA package on /data that has been (possibly
+            // partially) processed. (Bug: 24973532)
+            if (filename != null && filename.startsWith("/data")) {
+                if (UNCRYPT_PACKAGE_FILE.delete()) {
+                    Log.i(TAG, "Deleted: " + filename);
+                } else {
+                    Log.e(TAG, "Can't delete: " + filename);
+                }
+            }
+        }
+
+        // We keep the update logs (beginning with LAST_PREFIX), and optionally
+        // the block map file (BLOCK_MAP_FILE) for a package. BLOCK_MAP_FILE
+        // will be created at the end of a successful uncrypt. If seeing this
+        // file, we keep the block map file and the file that contains the
+        // package name (UNCRYPT_PACKAGE_FILE). This is to reduce the work for
+        // GmsCore to avoid re-downloading everything again.
+        String[] names = RECOVERY_DIR.list();
+        for (int i = 0; names != null && i < names.length; i++) {
+            // Do not remove the last_install file since the recovery-persist takes care of it.
+            if (names[i].startsWith(LAST_PREFIX) || names[i].equals(LAST_INSTALL_PATH)) continue;
+            if (reservePackage && names[i].equals(BLOCK_MAP_FILE.getName())) continue;
+            if (reservePackage && names[i].equals(UNCRYPT_PACKAGE_FILE.getName())) continue;
+
+            recursiveDelete(new File(RECOVERY_DIR, names[i]));
+        }
+
+        return log;
+    }
+
+    /**
+     * Internally, delete a given file or directory recursively.
+     */
+    private static void recursiveDelete(File name) {
+        if (name.isDirectory()) {
+            String[] files = name.list();
+            for (int i = 0; files != null && i < files.length; i++) {
+                File f = new File(name, files[i]);
+                recursiveDelete(f);
+            }
+        }
+
+        if (!name.delete()) {
+            Log.e(TAG, "Can't delete: " + name);
+        } else {
+            Log.i(TAG, "Deleted: " + name);
+        }
+    }
+
+    /**
+     * Talks to RecoverySystemService via Binder to trigger uncrypt.
+     */
+    private boolean uncrypt(String packageFile, IRecoverySystemProgressListener listener) {
+        try {
+            return mService.uncrypt(packageFile, listener);
+        } catch (RemoteException unused) {
+        }
+        return false;
+    }
+
+    /**
+     * Talks to RecoverySystemService via Binder to set up the BCB.
+     */
+    private boolean setupBcb(String command) {
+        try {
+            return mService.setupBcb(command);
+        } catch (RemoteException unused) {
+        }
+        return false;
+    }
+
+    /**
+     * Talks to RecoverySystemService via Binder to allocate space
+     */
+    private boolean allocateSpaceForUpdate(File packageFile) throws RemoteException {
+        return mService.allocateSpaceForUpdate(packageFile.getAbsolutePath());
+    }
+
+    /**
+     * Talks to RecoverySystemService via Binder to clear up the BCB.
+     */
+    private boolean clearBcb() {
+        try {
+            return mService.clearBcb();
+        } catch (RemoteException unused) {
+        }
+        return false;
+    }
+
+    /**
+     * Talks to RecoverySystemService via Binder to set up the BCB command and
+     * reboot into recovery accordingly.
+     */
+    private void rebootRecoveryWithCommand(String command) {
+        try {
+            mService.rebootRecoveryWithCommand(command);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
+     * Begins the process of asking the user for the Lock Screen Knowledge Factor.
+     *
+     * @param packageName the package name of the caller who requests Resume on Reboot
+     * @return true if the request was correct
+     * @throws IOException if the recovery system service could not be contacted
+     */
+    private boolean requestLskf(String packageName, IntentSender sender) throws IOException {
+        try {
+            return mService.requestLskf(packageName, sender);
+        } catch (RemoteException | SecurityException e) {
+            throw new IOException("could not request LSKF capture", e);
+        }
+    }
+
+    /**
+     * Calls the recovery system service and clears the setup for the OTA.
+     *
+     * @return true if the setup for OTA was cleared
+     * @throws IOException if the recovery system service could not be contacted
+     */
+    private boolean clearLskf(String packageName) throws IOException {
+        try {
+            return mService.clearLskf(packageName);
+        } catch (RemoteException | SecurityException e) {
+            throw new IOException("could not clear LSKF", e);
+        }
+    }
+
+    /**
+     * Queries if the Resume on Reboot has been prepared for a given caller.
+     *
+     * @param packageName the identifier of the caller who requests Resume on Reboot
+     * @return true if Resume on Reboot is prepared.
+     * @throws IOException if the recovery system service could not be contacted
+     */
+    private boolean isLskfCaptured(String packageName) throws IOException {
+        try {
+            return mService.isLskfCaptured(packageName);
+        } catch (RemoteException | SecurityException e) {
+            throw new IOException("could not get LSKF capture state", e);
+        }
+    }
+
+    /**
+     * Calls the recovery system service to reboot and apply update.
+     *
+     */
+    private @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason,
+            boolean slotSwitch) throws IOException {
+        try {
+            return mService.rebootWithLskf(packageName, reason, slotSwitch);
+        } catch (RemoteException | SecurityException e) {
+            throw new IOException("could not reboot for update", e);
+        }
+    }
+
+    /**
+     * Calls the recovery system service to reboot and apply update. This is the legacy API and
+     * expects a slot switch for A/B devices.
+     *
+     */
+    private @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName,
+            String reason) throws IOException {
+        try {
+            return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason);
+        } catch (RemoteException | RuntimeException e) {
+            throw new IOException("could not reboot for update", e);
+        }
+    }
+
+    /**
+     * Internally, recovery treats each line of the command file as a separate
+     * argv, so we only need to protect against newlines and nulls.
+     */
+    private static String sanitizeArg(String arg) {
+        arg = arg.replace('\0', '?');
+        arg = arg.replace('\n', '?');
+        return arg;
+    }
+
+
+    /**
+     * @removed Was previously made visible by accident.
+     */
+    public RecoverySystem() {
+        mService = null;
+    }
+
+    /**
+     * @hide
+     */
+    public RecoverySystem(IRecoverySystem service) {
+        mService = service;
+    }
+}
diff --git a/android-34/android/os/RedactingFileDescriptor.java b/android-34/android/os/RedactingFileDescriptor.java
new file mode 100644
index 0000000..a1ed214
--- /dev/null
+++ b/android-34/android/os/RedactingFileDescriptor.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.Arrays;
+
+/**
+ * Variant of {@link FileDescriptor} that allows its creator to specify regions
+ * that should be redacted.
+ *
+ * @hide
+ */
+public class RedactingFileDescriptor {
+    private static final String TAG = "RedactingFileDescriptor";
+    private static final boolean DEBUG = true;
+
+    private volatile long[] mRedactRanges;
+    private volatile long[] mFreeOffsets;
+
+    private FileDescriptor mInner = null;
+    private ParcelFileDescriptor mOuter = null;
+
+    private RedactingFileDescriptor(
+            Context context, File file, int mode, long[] redactRanges, long[] freeOffsets)
+            throws IOException {
+        mRedactRanges = checkRangesArgument(redactRanges);
+        mFreeOffsets = freeOffsets;
+
+        try {
+            try {
+                mInner = Os.open(file.getAbsolutePath(),
+                        FileUtils.translateModePfdToPosix(mode), 0);
+                mOuter = context.getSystemService(StorageManager.class)
+                        .openProxyFileDescriptor(mode, mCallback);
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        } catch (IOException e) {
+            IoUtils.closeQuietly(mInner);
+            IoUtils.closeQuietly(mOuter);
+            throw e;
+        }
+    }
+
+    private static long[] checkRangesArgument(long[] ranges) {
+        if (ranges.length % 2 != 0) {
+            throw new IllegalArgumentException();
+        }
+        for (int i = 0; i < ranges.length - 1; i += 2) {
+            if (ranges[i] > ranges[i + 1]) {
+                throw new IllegalArgumentException();
+            }
+        }
+        return ranges;
+    }
+
+    /**
+     * Open the given {@link File} and returns a {@link ParcelFileDescriptor}
+     * that offers a redacted view of the underlying data. If a redacted region
+     * is written to, the newly written data can be read back correctly instead
+     * of continuing to be redacted.
+     *
+     * @param file The underlying file to open.
+     * @param mode The {@link ParcelFileDescriptor} mode to open with.
+     * @param redactRanges List of file ranges that should be redacted, stored
+     *            as {@code [start1, end1, start2, end2, ...]}. Start values are
+     *            inclusive and end values are exclusive.
+     * @param freePositions List of file offsets at which the four byte value 'free' should be
+     *            written instead of zeros within parts of the file covered by {@code redactRanges}.
+     *            Non-redacted bytes will not be modified even if covered by a 'free'. This is
+     *            useful for overwriting boxes in ISOBMFF files with padding data.
+     */
+    public static ParcelFileDescriptor open(Context context, File file, int mode,
+            long[] redactRanges, long[] freePositions) throws IOException {
+        return new RedactingFileDescriptor(context, file, mode, redactRanges, freePositions).mOuter;
+    }
+
+    /**
+     * Update the given ranges argument to remove any references to the given
+     * offset and length. This is typically used when a caller has written over
+     * a previously redacted region.
+     */
+    @VisibleForTesting
+    public static long[] removeRange(long[] ranges, long start, long end) {
+        if (start == end) {
+            return ranges;
+        } else if (start > end) {
+            throw new IllegalArgumentException();
+        }
+
+        long[] res = EmptyArray.LONG;
+        for (int i = 0; i < ranges.length; i += 2) {
+            if (start <= ranges[i] && end >= ranges[i + 1]) {
+                // Range entirely covered; remove it
+            } else if (start >= ranges[i] && end <= ranges[i + 1]) {
+                // Range partially covered; punch a hole
+                res = Arrays.copyOf(res, res.length + 4);
+                res[res.length - 4] = ranges[i];
+                res[res.length - 3] = start;
+                res[res.length - 2] = end;
+                res[res.length - 1] = ranges[i + 1];
+            } else {
+                // Range might covered; adjust edges if needed
+                res = Arrays.copyOf(res, res.length + 2);
+                if (end >= ranges[i] && end <= ranges[i + 1]) {
+                    res[res.length - 2] = Math.max(ranges[i], end);
+                } else {
+                    res[res.length - 2] = ranges[i];
+                }
+                if (start >= ranges[i] && start <= ranges[i + 1]) {
+                    res[res.length - 1] = Math.min(ranges[i + 1], start);
+                } else {
+                    res[res.length - 1] = ranges[i + 1];
+                }
+            }
+        }
+        return res;
+    }
+
+    private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
+        @Override
+        public long onGetSize() throws ErrnoException {
+            return Os.fstat(mInner).st_size;
+        }
+
+        @Override
+        public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+            int n = 0;
+            while (n < size) {
+                try {
+                    final int res = Os.pread(mInner, data, n, size - n, offset + n);
+                    if (res == 0) {
+                        break;
+                    } else {
+                        n += res;
+                    }
+                } catch (InterruptedIOException e) {
+                    n += e.bytesTransferred;
+                }
+            }
+
+            // Redact any relevant ranges before returning
+            final long[] ranges = mRedactRanges;
+            for (int i = 0; i < ranges.length; i += 2) {
+                final long start = Math.max(offset, ranges[i]);
+                final long end = Math.min(offset + size, ranges[i + 1]);
+                for (long j = start; j < end; j++) {
+                    data[(int) (j - offset)] = 0;
+                }
+                // Overwrite data at 'free' offsets within the redaction ranges.
+                for (long freeOffset : mFreeOffsets) {
+                    final long freeEnd = freeOffset + 4;
+                    final long redactFreeStart = Math.max(freeOffset, start);
+                    final long redactFreeEnd = Math.min(freeEnd, end);
+                    for (long j = redactFreeStart; j < redactFreeEnd; j++) {
+                        data[(int) (j - offset)] = (byte) "free".charAt((int) (j - freeOffset));
+                    }
+                }
+            }
+            return n;
+        }
+
+        @Override
+        public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+            int n = 0;
+            while (n < size) {
+                try {
+                    final int res = Os.pwrite(mInner, data, n, size - n, offset + n);
+                    if (res == 0) {
+                        break;
+                    } else {
+                        n += res;
+                    }
+                } catch (InterruptedIOException e) {
+                    n += e.bytesTransferred;
+                }
+            }
+
+            // Clear any relevant redaction ranges before returning, since the
+            // writer should have access to see the data they just overwrote
+            mRedactRanges = removeRange(mRedactRanges, offset, offset + n);
+            return n;
+        }
+
+        @Override
+        public void onFsync() throws ErrnoException {
+            Os.fsync(mInner);
+        }
+
+        @Override
+        public void onRelease() {
+            if (DEBUG) Slog.v(TAG, "onRelease()");
+            IoUtils.closeQuietly(mInner);
+        }
+    };
+}
diff --git a/android-34/android/os/Registrant.java b/android-34/android/os/Registrant.java
new file mode 100644
index 0000000..bde7ec1
--- /dev/null
+++ b/android-34/android/os/Registrant.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import java.lang.ref.WeakReference;
+
+/** @hide */
+public class Registrant
+{
+    @UnsupportedAppUsage
+    public
+    Registrant(Handler h, int what, Object obj)
+    {
+        refH = new WeakReference(h);
+        this.what = what;
+        userObj = obj;
+    }
+
+    @UnsupportedAppUsage
+    public void
+    clear()
+    {
+        refH = null;
+        userObj = null;
+    }
+
+    @UnsupportedAppUsage
+    public void
+    notifyRegistrant()
+    {
+        internalNotifyRegistrant (null, null);
+    }
+
+    @UnsupportedAppUsage
+    public void
+    notifyResult(Object result)
+    {
+        internalNotifyRegistrant (result, null);
+    }
+
+    public void
+    notifyException(Throwable exception)
+    {
+        internalNotifyRegistrant (null, exception);
+    }
+
+    /**
+     * This makes a copy of @param ar
+     */
+    @UnsupportedAppUsage
+    public void
+    notifyRegistrant(AsyncResult ar)
+    {
+        internalNotifyRegistrant (ar.result, ar.exception);
+    }
+
+    /*package*/ void
+    internalNotifyRegistrant (Object result, Throwable exception)
+    {
+        Handler h = getHandler();
+
+        if (h == null) {
+            clear();
+        } else {
+            Message msg = Message.obtain();
+
+            msg.what = what;
+            msg.obj = new AsyncResult(userObj, result, exception);
+            h.sendMessage(msg);
+        }
+    }
+
+    /**
+     * NOTE: May return null if weak reference has been collected
+     */
+
+    @UnsupportedAppUsage
+    public Message
+    messageForRegistrant()
+    {
+        Handler h = getHandler();
+
+        if (h == null) {
+            clear();
+
+            return null;
+        } else {
+            Message msg = h.obtainMessage();
+
+            msg.what = what;
+            msg.obj = userObj;
+
+            return msg;
+        }
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public Handler
+    getHandler()
+    {
+        if (refH == null)
+            return null;
+
+        return (Handler) refH.get();
+    }
+
+    WeakReference   refH;
+    int             what;
+    Object          userObj;
+}
diff --git a/android-34/android/os/RegistrantList.java b/android-34/android/os/RegistrantList.java
new file mode 100644
index 0000000..b36734b
--- /dev/null
+++ b/android-34/android/os/RegistrantList.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import java.util.ArrayList;
+
+/** @hide */
+public class RegistrantList
+{
+    ArrayList   registrants = new ArrayList();      // of Registrant
+
+    @UnsupportedAppUsage
+    public RegistrantList() {
+    }
+
+    @UnsupportedAppUsage
+    public synchronized void
+    add(Handler h, int what, Object obj)
+    {
+        add(new Registrant(h, what, obj));
+    }
+
+    @UnsupportedAppUsage
+    public synchronized void
+    addUnique(Handler h, int what, Object obj)
+    {
+        // if the handler is already in the registrant list, remove it
+        remove(h);
+        add(new Registrant(h, what, obj));
+    }
+
+    @UnsupportedAppUsage
+    public synchronized void
+    add(Registrant r)
+    {
+        removeCleared();
+        registrants.add(r);
+    }
+
+    @UnsupportedAppUsage
+    public synchronized void
+    removeCleared()
+    {
+        for (int i = registrants.size() - 1; i >= 0 ; i--) {
+            Registrant  r = (Registrant) registrants.get(i);
+
+            if (r.refH == null) {
+                registrants.remove(i);
+            }
+        }
+    }
+
+    public synchronized void removeAll() {
+        registrants.clear();
+    }
+
+    @UnsupportedAppUsage
+    public synchronized int
+    size()
+    {
+        return registrants.size();
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public synchronized Object
+    get(int index)
+    {
+        return registrants.get(index);
+    }
+
+    private synchronized void
+    internalNotifyRegistrants (Object result, Throwable exception)
+    {
+       for (int i = 0, s = registrants.size(); i < s ; i++) {
+            Registrant  r = (Registrant) registrants.get(i);
+            r.internalNotifyRegistrant(result, exception);
+       }
+    }
+
+    @UnsupportedAppUsage
+    public /*synchronized*/ void
+    notifyRegistrants()
+    {
+        internalNotifyRegistrants(null, null);
+    }
+
+    public /*synchronized*/ void
+    notifyException(Throwable exception)
+    {
+        internalNotifyRegistrants (null, exception);
+    }
+
+    @UnsupportedAppUsage
+    public /*synchronized*/ void
+    notifyResult(Object result)
+    {
+        internalNotifyRegistrants (result, null);
+    }
+
+
+    @UnsupportedAppUsage
+    public /*synchronized*/ void
+    notifyRegistrants(AsyncResult ar)
+    {
+        internalNotifyRegistrants(ar.result, ar.exception);
+    }
+
+    @UnsupportedAppUsage
+    public synchronized void
+    remove(Handler h)
+    {
+        for (int i = 0, s = registrants.size() ; i < s ; i++) {
+            Registrant  r = (Registrant) registrants.get(i);
+            Handler     rh;
+
+            rh = r.getHandler();
+
+            /* Clean up both the requested registrant and
+             * any now-collected registrants
+             */
+            if (rh == null || rh == h) {
+                r.clear();
+            }
+        }
+
+        removeCleared();
+    }
+}
diff --git a/android-34/android/os/RemoteCallback.java b/android-34/android/os/RemoteCallback.java
new file mode 100644
index 0000000..49f84ad
--- /dev/null
+++ b/android-34/android/os/RemoteCallback.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class RemoteCallback implements Parcelable {
+
+    public interface OnResultListener {
+        void onResult(@Nullable Bundle result);
+    }
+
+    private final OnResultListener mListener;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private final Handler mHandler;
+    private final IRemoteCallback mCallback;
+
+    public RemoteCallback(OnResultListener listener) {
+        this(listener, null);
+    }
+
+    public RemoteCallback(@NonNull OnResultListener listener, @Nullable Handler handler) {
+        if (listener == null) {
+            throw new NullPointerException("listener cannot be null");
+        }
+        mListener = listener;
+        mHandler = handler;
+        mCallback = new IRemoteCallback.Stub() {
+            @Override
+            public void sendResult(Bundle data) {
+                RemoteCallback.this.sendResult(data);
+            }
+        };
+    }
+
+    RemoteCallback(Parcel parcel) {
+        mListener = null;
+        mHandler = null;
+        mCallback = IRemoteCallback.Stub.asInterface(
+                parcel.readStrongBinder());
+    }
+
+    public void sendResult(@Nullable final Bundle result) {
+        // Do local dispatch
+        if (mListener != null) {
+            if (mHandler != null) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mListener.onResult(result);
+                    }
+                });
+            } else {
+                mListener.onResult(result);
+            }
+        // Do remote dispatch
+        } else {
+            try {
+                mCallback.sendResult(result);
+            } catch (RemoteException e) {
+                /* ignore */
+            }
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeStrongBinder(mCallback.asBinder());
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<RemoteCallback> CREATOR
+            = new Parcelable.Creator<RemoteCallback>() {
+        public RemoteCallback createFromParcel(Parcel parcel) {
+            return new RemoteCallback(parcel);
+        }
+
+        public RemoteCallback[] newArray(int size) {
+            return new RemoteCallback[size];
+        }
+    };
+}
diff --git a/android-34/android/os/RemoteCallbackList.java b/android-34/android/os/RemoteCallbackList.java
new file mode 100644
index 0000000..d89c3d5
--- /dev/null
+++ b/android-34/android/os/RemoteCallbackList.java
@@ -0,0 +1,470 @@
+/*
+ * 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.
+ * 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * Takes care of the grunt work of maintaining a list of remote interfaces,
+ * typically for the use of performing callbacks from a
+ * {@link android.app.Service} to its clients.  In particular, this:
+ *
+ * <ul>
+ * <li> Keeps track of a set of registered {@link IInterface} callbacks,
+ * taking care to identify them through their underlying unique {@link IBinder}
+ * (by calling {@link IInterface#asBinder IInterface.asBinder()}.
+ * <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
+ * each registered interface, so that it can be cleaned out of the list if its
+ * process goes away.
+ * <li> Performs locking of the underlying list of interfaces to deal with
+ * multithreaded incoming calls, and a thread-safe way to iterate over a
+ * snapshot of the list without holding its lock.
+ * </ul>
+ *
+ * <p>To use this class, simply create a single instance along with your
+ * service, and call its {@link #register} and {@link #unregister} methods
+ * as client register and unregister with your service.  To call back on to
+ * the registered clients, use {@link #beginBroadcast},
+ * {@link #getBroadcastItem}, and {@link #finishBroadcast}.
+ *
+ * <p>If a registered callback's process goes away, this class will take
+ * care of automatically removing it from the list.  If you want to do
+ * additional work in this situation, you can create a subclass that
+ * implements the {@link #onCallbackDied} method.
+ */
+public class RemoteCallbackList<E extends IInterface> {
+    private static final String TAG = "RemoteCallbackList";
+
+    @UnsupportedAppUsage
+    /*package*/ ArrayMap<IBinder, Callback> mCallbacks
+            = new ArrayMap<IBinder, Callback>();
+    private Object[] mActiveBroadcast;
+    private int mBroadcastCount = -1;
+    private boolean mKilled = false;
+    private StringBuilder mRecentCallers;
+
+    private final class Callback implements IBinder.DeathRecipient {
+        final E mCallback;
+        final Object mCookie;
+
+        Callback(E callback, Object cookie) {
+            mCallback = callback;
+            mCookie = cookie;
+        }
+
+        public void binderDied() {
+            synchronized (mCallbacks) {
+                mCallbacks.remove(mCallback.asBinder());
+            }
+            onCallbackDied(mCallback, mCookie);
+        }
+    }
+
+    /**
+     * Simple version of {@link RemoteCallbackList#register(E, Object)}
+     * that does not take a cookie object.
+     */
+    public boolean register(E callback) {
+        return register(callback, null);
+    }
+
+    /**
+     * Add a new callback to the list.  This callback will remain in the list
+     * until a corresponding call to {@link #unregister} or its hosting process
+     * goes away.  If the callback was already registered (determined by
+     * checking to see if the {@link IInterface#asBinder callback.asBinder()}
+     * object is already in the list), then it will be left as-is.
+     * Registrations are not counted; a single call to {@link #unregister}
+     * will remove a callback after any number calls to register it.
+     *
+     * @param callback The callback interface to be added to the list.  Must
+     * not be null -- passing null here will cause a NullPointerException.
+     * Most services will want to check for null before calling this with
+     * an object given from a client, so that clients can't crash the
+     * service with bad data.
+     *
+     * @param cookie Optional additional data to be associated with this
+     * callback.
+     * 
+     * @return Returns true if the callback was successfully added to the list.
+     * Returns false if it was not added, either because {@link #kill} had
+     * previously been called or the callback's process has gone away.
+     *
+     * @see #unregister
+     * @see #kill
+     * @see #onCallbackDied
+     */
+    public boolean register(E callback, Object cookie) {
+        synchronized (mCallbacks) {
+            if (mKilled) {
+                return false;
+            }
+            // Flag unusual case that could be caused by a leak. b/36778087
+            logExcessiveCallbacks();
+            IBinder binder = callback.asBinder();
+            try {
+                Callback cb = new Callback(callback, cookie);
+                unregister(callback);
+                binder.linkToDeath(cb, 0);
+                mCallbacks.put(binder, cb);
+                return true;
+            } catch (RemoteException e) {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Remove from the list a callback that was previously added with
+     * {@link #register}.  This uses the
+     * {@link IInterface#asBinder callback.asBinder()} object to correctly
+     * find the previous registration.
+     * Registrations are not counted; a single unregister call will remove
+     * a callback after any number calls to {@link #register} for it.
+     *
+     * @param callback The callback to be removed from the list.  Passing
+     * null here will cause a NullPointerException, so you will generally want
+     * to check for null before calling.
+     *
+     * @return Returns true if the callback was found and unregistered.  Returns
+     * false if the given callback was not found on the list.
+     *
+     * @see #register
+     */
+    public boolean unregister(E callback) {
+        synchronized (mCallbacks) {
+            Callback cb = mCallbacks.remove(callback.asBinder());
+            if (cb != null) {
+                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Disable this callback list.  All registered callbacks are unregistered,
+     * and the list is disabled so that future calls to {@link #register} will
+     * fail.  This should be used when a Service is stopping, to prevent clients
+     * from registering callbacks after it is stopped.
+     *
+     * @see #register
+     */
+    public void kill() {
+        synchronized (mCallbacks) {
+            for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
+                Callback cb = mCallbacks.valueAt(cbi);
+                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+            }
+            mCallbacks.clear();
+            mKilled = true;
+        }
+    }
+
+    /**
+     * Old version of {@link #onCallbackDied(E, Object)} that
+     * does not provide a cookie.
+     */
+    public void onCallbackDied(E callback) {
+    }
+    
+    /**
+     * Called when the process hosting a callback in the list has gone away.
+     * The default implementation calls {@link #onCallbackDied(E)}
+     * for backwards compatibility.
+     * 
+     * @param callback The callback whose process has died.  Note that, since
+     * its process has died, you can not make any calls on to this interface.
+     * You can, however, retrieve its IBinder and compare it with another
+     * IBinder to see if it is the same object.
+     * @param cookie The cookie object original provided to
+     * {@link #register(E, Object)}.
+     * 
+     * @see #register
+     */
+    public void onCallbackDied(E callback, Object cookie) {
+        onCallbackDied(callback);
+    }
+
+    /**
+     * Prepare to start making calls to the currently registered callbacks.
+     * This creates a copy of the callback list, which you can retrieve items
+     * from using {@link #getBroadcastItem}.  Note that only one broadcast can
+     * be active at a time, so you must be sure to always call this from the
+     * same thread (usually by scheduling with {@link Handler}) or
+     * do your own synchronization.  You must call {@link #finishBroadcast}
+     * when done.
+     *
+     * <p>A typical loop delivering a broadcast looks like this:
+     *
+     * <pre>
+     * int i = callbacks.beginBroadcast();
+     * while (i &gt; 0) {
+     *     i--;
+     *     try {
+     *         callbacks.getBroadcastItem(i).somethingHappened();
+     *     } catch (RemoteException e) {
+     *         // The RemoteCallbackList will take care of removing
+     *         // the dead object for us.
+     *     }
+     * }
+     * callbacks.finishBroadcast();</pre>
+     *
+     * @return Returns the number of callbacks in the broadcast, to be used
+     * with {@link #getBroadcastItem} to determine the range of indices you
+     * can supply.
+     *
+     * @see #getBroadcastItem
+     * @see #finishBroadcast
+     */
+    public int beginBroadcast() {
+        synchronized (mCallbacks) {
+            if (mBroadcastCount > 0) {
+                throw new IllegalStateException(
+                        "beginBroadcast() called while already in a broadcast");
+            }
+            
+            final int N = mBroadcastCount = mCallbacks.size();
+            if (N <= 0) {
+                return 0;
+            }
+            Object[] active = mActiveBroadcast;
+            if (active == null || active.length < N) {
+                mActiveBroadcast = active = new Object[N];
+            }
+            for (int i=0; i<N; i++) {
+                active[i] = mCallbacks.valueAt(i);
+            }
+            return N;
+        }
+    }
+
+    /**
+     * Retrieve an item in the active broadcast that was previously started
+     * with {@link #beginBroadcast}.  This can <em>only</em> be called after
+     * the broadcast is started, and its data is no longer valid after
+     * calling {@link #finishBroadcast}.
+     *
+     * <p>Note that it is possible for the process of one of the returned
+     * callbacks to go away before you call it, so you will need to catch
+     * {@link RemoteException} when calling on to the returned object.
+     * The callback list itself, however, will take care of unregistering
+     * these objects once it detects that it is no longer valid, so you can
+     * handle such an exception by simply ignoring it.
+     *
+     * @param index Which of the registered callbacks you would like to
+     * retrieve.  Ranges from 0 to 1-{@link #beginBroadcast}.
+     *
+     * @return Returns the callback interface that you can call.  This will
+     * always be non-null.
+     *
+     * @see #beginBroadcast
+     */
+    public E getBroadcastItem(int index) {
+        return ((Callback)mActiveBroadcast[index]).mCallback;
+    }
+    
+    /**
+     * Retrieve the cookie associated with the item
+     * returned by {@link #getBroadcastItem(int)}.
+     * 
+     * @see #getBroadcastItem
+     */
+    public Object getBroadcastCookie(int index) {
+        return ((Callback)mActiveBroadcast[index]).mCookie;
+    }
+
+    /**
+     * Clean up the state of a broadcast previously initiated by calling
+     * {@link #beginBroadcast}.  This must always be called when you are done
+     * with a broadcast.
+     *
+     * @see #beginBroadcast
+     */
+    public void finishBroadcast() {
+        synchronized (mCallbacks) {
+            if (mBroadcastCount < 0) {
+                throw new IllegalStateException(
+                        "finishBroadcast() called outside of a broadcast");
+            }
+
+            Object[] active = mActiveBroadcast;
+            if (active != null) {
+                final int N = mBroadcastCount;
+                for (int i=0; i<N; i++) {
+                    active[i] = null;
+                }
+            }
+
+            mBroadcastCount = -1;
+        }
+    }
+
+    /**
+     * Performs {@code action} on each callback, calling
+     * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
+     *
+     * @hide
+     */
+    public void broadcast(Consumer<E> action) {
+        int itemCount = beginBroadcast();
+        try {
+            for (int i = 0; i < itemCount; i++) {
+                action.accept(getBroadcastItem(i));
+            }
+        } finally {
+            finishBroadcast();
+        }
+    }
+
+    /**
+     * Performs {@code action} for each cookie associated with a callback, calling
+     * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
+     *
+     * @hide
+     */
+    public <C> void broadcastForEachCookie(Consumer<C> action) {
+        int itemCount = beginBroadcast();
+        try {
+            for (int i = 0; i < itemCount; i++) {
+                action.accept((C) getBroadcastCookie(i));
+            }
+        } finally {
+            finishBroadcast();
+        }
+    }
+
+    /**
+     * Performs {@code action} on each callback and associated cookie, calling {@link
+     * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
+     *
+     * @hide
+     */
+    public <C> void broadcast(BiConsumer<E, C> action) {
+        int itemCount = beginBroadcast();
+        try {
+            for (int i = 0; i < itemCount; i++) {
+                action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+            }
+        } finally {
+            finishBroadcast();
+        }
+    }
+
+    /**
+     * Returns the number of registered callbacks. Note that the number of registered
+     * callbacks may differ from the value returned by {@link #beginBroadcast()} since
+     * the former returns the number of callbacks registered at the time of the call
+     * and the second the number of callback to which the broadcast will be delivered.
+     * <p>
+     * This function is useful to decide whether to schedule a broadcast if this
+     * requires doing some work which otherwise would not be performed.
+     * </p>
+     *
+     * @return The size.
+     */
+    public int getRegisteredCallbackCount() {
+        synchronized (mCallbacks) {
+            if (mKilled) {
+                return 0;
+            }
+            return mCallbacks.size();
+        }
+    }
+
+    /**
+     * Return a currently registered callback.  Note that this is
+     * <em>not</em> the same as {@link #getBroadcastItem} and should not be used
+     * interchangeably with it.  This method returns the registered callback at the given
+     * index, not the current broadcast state.  This means that it is not itself thread-safe:
+     * any call to {@link #register} or {@link #unregister} will change these indices, so you
+     * must do your own thread safety between these to protect from such changes.
+     *
+     * @param index Index of which callback registration to return, from 0 to
+     * {@link #getRegisteredCallbackCount()} - 1.
+     *
+     * @return Returns whatever callback is associated with this index, or null if
+     * {@link #kill()} has been called.
+     */
+    public E getRegisteredCallbackItem(int index) {
+        synchronized (mCallbacks) {
+            if (mKilled) {
+                return null;
+            }
+            return mCallbacks.valueAt(index).mCallback;
+        }
+    }
+
+    /**
+     * Return any cookie associated with a currently registered callback.  Note that this is
+     * <em>not</em> the same as {@link #getBroadcastCookie} and should not be used
+     * interchangeably with it.  This method returns the current cookie registered at the given
+     * index, not the current broadcast state.  This means that it is not itself thread-safe:
+     * any call to {@link #register} or {@link #unregister} will change these indices, so you
+     * must do your own thread safety between these to protect from such changes.
+     *
+     * @param index Index of which registration cookie to return, from 0 to
+     * {@link #getRegisteredCallbackCount()} - 1.
+     *
+     * @return Returns whatever cookie object is associated with this index, or null if
+     * {@link #kill()} has been called.
+     */
+    public Object getRegisteredCallbackCookie(int index) {
+        synchronized (mCallbacks) {
+            if (mKilled) {
+                return null;
+            }
+            return mCallbacks.valueAt(index).mCookie;
+        }
+    }
+
+    /** @hide */
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mCallbacks) {
+            pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
+            pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
+            pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
+        }
+    }
+
+    private void logExcessiveCallbacks() {
+        final long size = mCallbacks.size();
+        final long TOO_MANY = 3000;
+        final long MAX_CHARS = 1000;
+        if (size >= TOO_MANY) {
+            if (size == TOO_MANY && mRecentCallers == null) {
+                mRecentCallers = new StringBuilder();
+            }
+            if (mRecentCallers != null && mRecentCallers.length() < MAX_CHARS) {
+                mRecentCallers.append(Debug.getCallers(5));
+                mRecentCallers.append('\n');
+                if (mRecentCallers.length() >= MAX_CHARS) {
+                    Slog.wtf(TAG, "More than "
+                            + TOO_MANY + " remote callbacks registered. Recent callers:\n"
+                            + mRecentCallers.toString());
+                    mRecentCallers = null;
+                }
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/RemoteException.java b/android-34/android/os/RemoteException.java
new file mode 100644
index 0000000..970f419
--- /dev/null
+++ b/android-34/android/os/RemoteException.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.util.AndroidException;
+
+/**
+ * Parent exception for all Binder remote-invocation errors
+ *
+ * Note: not all exceptions from binder services will be subclasses of this.
+ *   For instance, RuntimeException and several subclasses of it may be
+ *   thrown as well as OutOfMemoryException.
+ *
+ * One common subclass is {@link DeadObjectException}.
+ */
+public class RemoteException extends AndroidException {
+    public RemoteException() {
+        super();
+    }
+
+    public RemoteException(String message) {
+        super(message);
+    }
+
+    /** @hide */
+    public RemoteException(String message, Throwable cause, boolean enableSuppression,
+            boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+    /** @hide */
+    public RemoteException(Throwable cause) {
+        this(cause.getMessage(), cause, true, false);
+    }
+
+    /**
+     * Rethrow this as an unchecked runtime exception.
+     * <p>
+     * Apps making calls into other processes may end up persisting internal
+     * state or making security decisions based on the perceived success or
+     * failure of a call, or any default values returned. For this reason, we
+     * want to strongly throw when there was trouble with the transaction.
+     *
+     * @throws RuntimeException
+     */
+    @NonNull
+    public RuntimeException rethrowAsRuntimeException() {
+        throw new RuntimeException(this);
+    }
+
+    /**
+     * Rethrow this exception when we know it came from the system server. This
+     * gives us an opportunity to throw a nice clean
+     * {@link DeadSystemRuntimeException} signal to avoid spamming logs with
+     * misleading stack traces.
+     * <p>
+     * Apps making calls into the system server may end up persisting internal
+     * state or making security decisions based on the perceived success or
+     * failure of a call, or any default values returned. For this reason, we
+     * want to strongly throw when there was trouble with the transaction.
+     *
+     * @throws RuntimeException
+     */
+    @NonNull
+    public RuntimeException rethrowFromSystemServer() {
+        if (this instanceof DeadObjectException) {
+            throw new DeadSystemRuntimeException();
+        } else {
+            throw new RuntimeException(this);
+        }
+    }
+}
diff --git a/android-34/android/os/RemoteMailException.java b/android-34/android/os/RemoteMailException.java
new file mode 100644
index 0000000..1ac96d1
--- /dev/null
+++ b/android-34/android/os/RemoteMailException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+/** @hide */
+public class RemoteMailException extends Exception
+{
+    public RemoteMailException()
+    {
+    }
+
+    public RemoteMailException(String s)
+    {
+        super(s);
+    }
+}
+
diff --git a/android-34/android/os/ResultReceiver.java b/android-34/android/os/ResultReceiver.java
new file mode 100644
index 0000000..f2d8fe4
--- /dev/null
+++ b/android-34/android/os/ResultReceiver.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ * 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.os;
+
+import com.android.internal.os.IResultReceiver;
+
+/**
+ * Generic interface for receiving a callback result from someone.  Use this
+ * by creating a subclass and implement {@link #onReceiveResult}, which you can
+ * then pass to others and send through IPC, and receive results they
+ * supply with {@link #send}.
+ *
+ * <p>Note: the implementation underneath is just a simple wrapper around
+ * a {@link Binder} that is used to perform the communication.  This means
+ * semantically you should treat it as such: this class does not impact process
+ * lifecycle management (you must be using some higher-level component to tell
+ * the system that your process needs to continue running), the connection will
+ * break if your process goes away for any reason, etc.</p>
+ */
+public class ResultReceiver implements Parcelable {
+    final boolean mLocal;
+    final Handler mHandler;
+    
+    IResultReceiver mReceiver;
+    
+    class MyRunnable implements Runnable {
+        final int mResultCode;
+        final Bundle mResultData;
+        
+        MyRunnable(int resultCode, Bundle resultData) {
+            mResultCode = resultCode;
+            mResultData = resultData;
+        }
+        
+        public void run() {
+            onReceiveResult(mResultCode, mResultData);
+        }
+    }
+    
+    class MyResultReceiver extends IResultReceiver.Stub {
+        public void send(int resultCode, Bundle resultData) {
+            if (mHandler != null) {
+                mHandler.post(new MyRunnable(resultCode, resultData));
+            } else {
+                onReceiveResult(resultCode, resultData);
+            }
+        }
+    }
+    
+    /**
+     * Create a new ResultReceive to receive results.  Your
+     * {@link #onReceiveResult} method will be called from the thread running
+     * <var>handler</var> if given, or from an arbitrary thread if null.
+     */
+    public ResultReceiver(Handler handler) {
+        mLocal = true;
+        mHandler = handler;
+    }
+    
+    /**
+     * Deliver a result to this receiver.  Will call {@link #onReceiveResult},
+     * always asynchronously if the receiver has supplied a Handler in which
+     * to dispatch the result.
+     * @param resultCode Arbitrary result code to deliver, as defined by you.
+     * @param resultData Any additional data provided by you.
+     */
+    public void send(int resultCode, Bundle resultData) {
+        if (mLocal) {
+            if (mHandler != null) {
+                mHandler.post(new MyRunnable(resultCode, resultData));
+            } else {
+                onReceiveResult(resultCode, resultData);
+            }
+            return;
+        }
+        
+        if (mReceiver != null) {
+            try {
+                mReceiver.send(resultCode, resultData);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+    
+    /**
+     * Override to receive results delivered to this object.
+     * 
+     * @param resultCode Arbitrary result code delivered by the sender, as
+     * defined by the sender.
+     * @param resultData Any additional data provided by the sender.
+     */
+    protected void onReceiveResult(int resultCode, Bundle resultData) {
+    }
+    
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        synchronized (this) {
+            if (mReceiver == null) {
+                mReceiver = new MyResultReceiver();
+            }
+            out.writeStrongBinder(mReceiver.asBinder());
+        }
+    }
+
+    ResultReceiver(Parcel in) {
+        mLocal = false;
+        mHandler = null;
+        mReceiver = IResultReceiver.Stub.asInterface(in.readStrongBinder());
+    }
+    
+    public static final @android.annotation.NonNull Parcelable.Creator<ResultReceiver> CREATOR
+            = new Parcelable.Creator<ResultReceiver>() {
+        public ResultReceiver createFromParcel(Parcel in) {
+            return new ResultReceiver(in);
+        }
+        public ResultReceiver[] newArray(int size) {
+            return new ResultReceiver[size];
+        }
+    };
+}
diff --git a/android-34/android/os/RevocableFileDescriptor.java b/android-34/android/os/RevocableFileDescriptor.java
new file mode 100644
index 0000000..ac2cd60
--- /dev/null
+++ b/android-34/android/os/RevocableFileDescriptor.java
@@ -0,0 +1,174 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+/**
+ * Variant of {@link FileDescriptor} that allows its creator to revoke all
+ * access to the underlying resource.
+ * <p>
+ * This is useful when the code that originally opened a file needs to strongly
+ * assert that any clients are completely hands-off for security purposes.
+ *
+ * @hide
+ */
+public class RevocableFileDescriptor {
+    private static final String TAG = "RevocableFileDescriptor";
+    private static final boolean DEBUG = true;
+
+    private FileDescriptor mInner;
+    private ParcelFileDescriptor mOuter;
+
+    private volatile boolean mRevoked;
+
+    private ParcelFileDescriptor.OnCloseListener mOnCloseListener;
+
+    /** {@hide} */
+    public RevocableFileDescriptor() {
+    }
+
+    /**
+     * Create an instance that references the given {@link File}.
+     */
+    public RevocableFileDescriptor(Context context, File file) throws IOException {
+        try {
+            init(context, Os.open(file.getAbsolutePath(),
+                    OsConstants.O_CREAT | OsConstants.O_RDWR, 0700));
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
+    /**
+     * Create an instance that references the given {@link FileDescriptor}.
+     */
+    public RevocableFileDescriptor(Context context, FileDescriptor fd) throws IOException {
+        init(context, fd);
+    }
+
+    /** {@hide} */
+    public void init(Context context, FileDescriptor fd) throws IOException {
+        mInner = fd;
+        mOuter = context.getSystemService(StorageManager.class)
+                .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+    }
+
+    /**
+     * Return a {@link ParcelFileDescriptor} which can safely be passed to an
+     * untrusted process. After {@link #revoke()} is called, all operations will
+     * fail with {@link OsConstants#EPERM}.
+     */
+    public ParcelFileDescriptor getRevocableFileDescriptor() {
+        return mOuter;
+    }
+
+    /**
+     * Revoke all future access to the {@link ParcelFileDescriptor} returned by
+     * {@link #getRevocableFileDescriptor()}. From this point forward, all
+     * operations will fail with {@link OsConstants#EPERM}.
+     */
+    public void revoke() {
+        mRevoked = true;
+        IoUtils.closeQuietly(mInner);
+    }
+
+    /**
+     * Callback for indicating that {@link ParcelFileDescriptor} passed to the client
+     * process ({@link #getRevocableFileDescriptor()}) has been closed.
+     */
+    public void addOnCloseListener(ParcelFileDescriptor.OnCloseListener onCloseListener) {
+        mOnCloseListener = onCloseListener;
+    }
+
+    public boolean isRevoked() {
+        return mRevoked;
+    }
+
+    private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
+        private void checkRevoked() throws ErrnoException {
+            if (mRevoked) {
+                throw new ErrnoException(TAG, OsConstants.EPERM);
+            }
+        }
+
+        @Override
+        public long onGetSize() throws ErrnoException {
+            checkRevoked();
+            return Os.fstat(mInner).st_size;
+        }
+
+        @Override
+        public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+            checkRevoked();
+            int n = 0;
+            while (n < size) {
+                try {
+                    n += Os.pread(mInner, data, n, size - n, offset + n);
+                    break;
+                } catch (InterruptedIOException e) {
+                    n += e.bytesTransferred;
+                }
+            }
+            return n;
+        }
+
+        @Override
+        public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+            checkRevoked();
+            int n = 0;
+            while (n < size) {
+                try {
+                    n += Os.pwrite(mInner, data, n, size - n, offset + n);
+                    break;
+                } catch (InterruptedIOException e) {
+                    n += e.bytesTransferred;
+                }
+            }
+            return n;
+        }
+
+        @Override
+        public void onFsync() throws ErrnoException {
+            if (DEBUG) Slog.v(TAG, "onFsync()");
+            checkRevoked();
+            Os.fsync(mInner);
+        }
+
+        @Override
+        public void onRelease() {
+            if (DEBUG) Slog.v(TAG, "onRelease()");
+            mRevoked = true;
+            IoUtils.closeQuietly(mInner);
+            if (mOnCloseListener != null) {
+                mOnCloseListener.onClose(null);
+            }
+        }
+    };
+}
diff --git a/android-34/android/os/SELinux.java b/android-34/android/os/SELinux.java
new file mode 100644
index 0000000..f64a811
--- /dev/null
+++ b/android-34/android/os/SELinux.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * This class provides access to the centralized jni bindings for
+ * SELinux interaction.
+ * {@hide}
+ */
+public class SELinux {
+    private static final String TAG = "SELinux";
+
+    /** Keep in sync with ./external/selinux/libselinux/include/selinux/android.h */
+    private static final int SELINUX_ANDROID_RESTORECON_NOCHANGE = 1;
+    private static final int SELINUX_ANDROID_RESTORECON_VERBOSE = 2;
+    private static final int SELINUX_ANDROID_RESTORECON_RECURSE = 4;
+    private static final int SELINUX_ANDROID_RESTORECON_FORCE = 8;
+    private static final int SELINUX_ANDROID_RESTORECON_DATADATA = 16;
+    private static final int SELINUX_ANDROID_RESTORECON_SKIPCE = 32;
+    private static final int SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS = 64;
+    private static final int SELINUX_ANDROID_RESTORECON_SKIP_SEHASH = 128;
+
+    /**
+     * Get context associated with path by file_contexts.
+     * @param path path to the regular file to get the security context for.
+     * @return a String representing the security context or null on failure.
+     */
+    public static final native String fileSelabelLookup(String path);
+
+    /**
+     * Determine whether SELinux is disabled or enabled.
+     * @return a boolean indicating whether SELinux is enabled.
+     */
+    @UnsupportedAppUsage
+    public static final native boolean isSELinuxEnabled();
+
+    /**
+     * Determine whether SELinux is permissive or enforcing.
+     * @return a boolean indicating whether SELinux is enforcing.
+     */
+    @UnsupportedAppUsage
+    public static final native boolean isSELinuxEnforced();
+
+    /**
+     * Sets the security context for newly created file objects.
+     * @param context a security context given as a String.
+     * @return a boolean indicating whether the operation succeeded.
+     */
+    public static final native boolean setFSCreateContext(String context);
+
+    /**
+     * Change the security context of an existing file object.
+     * @param path representing the path of file object to relabel.
+     * @param context new security context given as a String.
+     * @return a boolean indicating whether the operation succeeded.
+     */
+    public static final native boolean setFileContext(String path, String context);
+
+    /**
+     * Get the security context of a file object.
+     * @param path the pathname of the file object.
+     * @return a security context given as a String.
+     */
+    @UnsupportedAppUsage
+    public static final native String getFileContext(String path);
+
+    /**
+     * Get the security context of a peer socket.
+     * @param fd FileDescriptor class of the peer socket.
+     * @return a String representing the peer socket security context.
+     */
+    public static final native String getPeerContext(FileDescriptor fd);
+
+    /**
+     * Get the security context of a file descriptor of a file.
+     * @param fd FileDescriptor of a file.
+     * @return a String representing the file descriptor security context.
+     */
+    public static final native String getFileContext(FileDescriptor fd);
+
+    /**
+     * Gets the security context of the current process.
+     * @return a String representing the security context of the current process.
+     */
+    @UnsupportedAppUsage
+    public static final native String getContext();
+
+    /**
+     * Gets the security context of a given process id.
+     * @param pid an int representing the process id to check.
+     * @return a String representing the security context of the given pid.
+     */
+    @UnsupportedAppUsage
+    public static final native String getPidContext(int pid);
+
+    /**
+     * Check permissions between two security contexts.
+     * @param scon The source or subject security context.
+     * @param tcon The target or object security context.
+     * @param tclass The object security class name.
+     * @param perm The permission name.
+     * @return a boolean indicating whether permission was granted.
+     */
+    @UnsupportedAppUsage
+    public static final native boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm);
+
+    /**
+     * Restores a file to its default SELinux security context.
+     * If the system is not compiled with SELinux, then {@code true}
+     * is automatically returned.
+     * If SELinux is compiled in, but disabled, then {@code true} is
+     * returned.
+     *
+     * @param pathname The pathname of the file to be relabeled.
+     * @return a boolean indicating whether the relabeling succeeded.
+     * @exception NullPointerException if the pathname is a null object.
+     */
+    public static boolean restorecon(String pathname) throws NullPointerException {
+        if (pathname == null) { throw new NullPointerException(); }
+        return native_restorecon(pathname, 0);
+    }
+
+    /**
+     * Restores a file to its default SELinux security context.
+     * If the system is not compiled with SELinux, then {@code true}
+     * is automatically returned.
+     * If SELinux is compiled in, but disabled, then {@code true} is
+     * returned.
+     *
+     * @param pathname The pathname of the file to be relabeled.
+     * @return a boolean indicating whether the relabeling succeeded.
+     */
+    private static native boolean native_restorecon(String pathname, int flags);
+
+    /**
+     * Restores a file to its default SELinux security context.
+     * If the system is not compiled with SELinux, then {@code true}
+     * is automatically returned.
+     * If SELinux is compiled in, but disabled, then {@code true} is
+     * returned.
+     *
+     * @param file The File object representing the path to be relabeled.
+     * @return a boolean indicating whether the relabeling succeeded.
+     * @exception NullPointerException if the file is a null object.
+     */
+    public static boolean restorecon(File file) throws NullPointerException {
+        try {
+            return native_restorecon(file.getCanonicalPath(), 0);
+        } catch (IOException e) {
+            Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
+                    file.getPath(), e);
+            return false;
+        }
+    }
+
+    /**
+     * Recursively restores all files under the given path to their default
+     * SELinux security context. If the system is not compiled with SELinux,
+     * then {@code true} is automatically returned. If SELinux is compiled in,
+     * but disabled, then {@code true} is returned.
+     *
+     * @return a boolean indicating whether the relabeling succeeded.
+     */
+    @UnsupportedAppUsage
+    public static boolean restoreconRecursive(File file) {
+        try {
+            return native_restorecon(file.getCanonicalPath(),
+                SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
+        } catch (IOException e) {
+            Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
+                    file.getPath(), e);
+            return false;
+        }
+    }
+}
diff --git a/android-34/android/os/ServiceManager.java b/android-34/android/os/ServiceManager.java
new file mode 100644
index 0000000..b210c46
--- /dev/null
+++ b/android-34/android/os/ServiceManager.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderInternal;
+import com.android.internal.util.StatLogger;
+
+import java.util.Map;
+
+/**
+ * Manage binder services as registered with the binder context manager. These services must be
+ * declared statically on an Android device (SELinux access_vector service_manager, w/ service
+ * names in service_contexts files), and they do not follow the activity lifecycle. When
+ * building applications, android.app.Service should be preferred.
+ *
+ * @hide
+ **/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ServiceManager {
+    private static final String TAG = "ServiceManager";
+    private static final Object sLock = new Object();
+
+    @UnsupportedAppUsage
+    private static IServiceManager sServiceManager;
+
+    /**
+     * Cache for the "well known" services, such as WM and AM.
+     */
+    @UnsupportedAppUsage
+    private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();
+
+    /**
+     * We do the "slow log" at most once every this interval.
+     */
+    private static final int SLOW_LOG_INTERVAL_MS = 5000;
+
+    /**
+     * We do the "stats log" at most once every this interval.
+     */
+    private static final int STATS_LOG_INTERVAL_MS = 5000;
+
+    /**
+     * Threshold in uS for a "slow" call, used on core UIDs. We use a more relax value to
+     * avoid logspam.
+     */
+    private static final long GET_SERVICE_SLOW_THRESHOLD_US_CORE =
+            SystemProperties.getInt("debug.servicemanager.slow_call_core_ms", 10) * 1000;
+
+    /**
+     * Threshold in uS for a "slow" call, used on non-core UIDs. We use a more relax value to
+     * avoid logspam.
+     */
+    private static final long GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE =
+            SystemProperties.getInt("debug.servicemanager.slow_call_ms", 50) * 1000;
+
+    /**
+     * We log stats logging ever this many getService() calls.
+     */
+    private static final int GET_SERVICE_LOG_EVERY_CALLS_CORE =
+            SystemProperties.getInt("debug.servicemanager.log_calls_core", 100);
+
+    /**
+     * We log stats logging ever this many getService() calls.
+     */
+    private static final int GET_SERVICE_LOG_EVERY_CALLS_NON_CORE =
+            SystemProperties.getInt("debug.servicemanager.log_calls", 200);
+
+    @GuardedBy("sLock")
+    private static int sGetServiceAccumulatedUs;
+
+    @GuardedBy("sLock")
+    private static int sGetServiceAccumulatedCallCount;
+
+    @GuardedBy("sLock")
+    private static long sLastStatsLogUptime;
+
+    @GuardedBy("sLock")
+    private static long sLastSlowLogUptime;
+
+    @GuardedBy("sLock")
+    private static long sLastSlowLogActualTime;
+
+    interface Stats {
+        int GET_SERVICE = 0;
+
+        int COUNT = GET_SERVICE + 1;
+    }
+
+    /** @hide */
+    public static final StatLogger sStatLogger = new StatLogger(new String[] {
+            "getService()",
+    });
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public ServiceManager() {
+    }
+
+    @UnsupportedAppUsage
+    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
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static IBinder getService(String name) {
+        try {
+            IBinder service = sCache.get(name);
+            if (service != null) {
+                return service;
+            } else {
+                return Binder.allowBlocking(rawGetService(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 ServiceNotFoundException} if none is found.
+     *
+     * @hide
+     */
+    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
+        final IBinder binder = getService(name);
+        if (binder != null) {
+            return binder;
+        } else {
+            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
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void addService(String name, IBinder service) {
+        addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
+    }
+
+    /**
+     * 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
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void addService(String name, IBinder service, boolean allowIsolated) {
+        addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
+    }
+
+    /**
+     * 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
+     * @param dumpPriority supported dump priority levels as a bitmask
+     * to access this service
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static void addService(String name, IBinder service, boolean allowIsolated,
+            int dumpPriority) {
+        try {
+            getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in addService", e);
+        }
+    }
+
+    /**
+     * Retrieve an existing service called @a name from the
+     * service manager.  Non-blocking.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    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;
+        }
+    }
+
+    /**
+     * Returns whether the specified service is declared.
+     *
+     * @return true if the service is declared somewhere (eg. VINTF manifest) and
+     * waitForService should always be able to return the service.
+     */
+    public static boolean isDeclared(@NonNull String name) {
+        try {
+            return getIServiceManager().isDeclared(name);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in isDeclared", e);
+            return false;
+        }
+    }
+
+    /**
+     * Returns an array of all declared instances for a particular interface.
+     *
+     * For instance, if 'android.foo.IFoo/foo' is declared (e.g. in VINTF
+     * manifest), and 'android.foo.IFoo' is passed here, then ["foo"] would be
+     * returned.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
+    public static String[] getDeclaredInstances(@NonNull String iface) {
+        try {
+            return getIServiceManager().getDeclaredInstances(iface);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in getDeclaredInstances", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the specified service from the service manager.
+     *
+     * If the service is not running, servicemanager will attempt to start it, and this function
+     * will wait for it to be ready.
+     *
+     * @return {@code null} only if there are permission problems or fatal errors.
+     * @hide
+     */
+    public static IBinder waitForService(@NonNull String name) {
+        return Binder.allowBlocking(waitForServiceNative(name));
+    }
+
+    private static native IBinder waitForServiceNative(@NonNull String name);
+
+    /**
+     * Returns the specified service from the service manager, if declared.
+     *
+     * If the service is not running, servicemanager will attempt to start it, and this function
+     * will wait for it to be ready.
+     *
+     * @throws SecurityException if the process does not have the permissions to check
+     * isDeclared() for the service.
+     * @return {@code null} if the service is not declared in the manifest, or if there
+     * are fatal errors.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @Nullable public static IBinder waitForDeclaredService(@NonNull String name) {
+        return isDeclared(name) ? waitForService(name) : null;
+    }
+
+    /**
+     * Register callback for service registration notifications.
+     *
+     * @throws RemoteException for underlying error.
+     * @hide
+     */
+    public static void registerForNotifications(
+            @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException {
+        getIServiceManager().registerForNotifications(name, callback);
+    }
+
+    /**
+     * Return a list of all currently running services.
+     * @return an array of all currently running services, or <code>null</code> in
+     * case of an exception
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String[] listServices() {
+        try {
+            return getIServiceManager().listServices(IServiceManager.DUMP_FLAG_PRIORITY_ALL);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in listServices", e);
+            return null;
+        }
+    }
+
+    /**
+     * Get service debug info.
+     * @return an array of information for each service (like listServices, but with PIDs)
+     * @hide
+     */
+    public static ServiceDebugInfo[] getServiceDebugInfo() {
+        try {
+            return getIServiceManager().getServiceDebugInfo();
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in getServiceDebugInfo", e);
+            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);
+    }
+
+    /**
+     * Exception thrown when no service published for given name. This might be
+     * thrown early during boot before certain services have published
+     * themselves.
+     *
+     * @hide
+     */
+    public static class ServiceNotFoundException extends Exception {
+        public ServiceNotFoundException(String name) {
+            super("No service published for: " + name);
+        }
+    }
+
+    private static IBinder rawGetService(String name) throws RemoteException {
+        final long start = sStatLogger.getTime();
+
+        final IBinder binder = getIServiceManager().getService(name);
+
+        final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
+
+        final int myUid = Process.myUid();
+        final boolean isCore = UserHandle.isCore(myUid);
+
+        final long slowThreshold = isCore
+                ? GET_SERVICE_SLOW_THRESHOLD_US_CORE
+                : GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE;
+
+        synchronized (sLock) {
+            sGetServiceAccumulatedUs += time;
+            sGetServiceAccumulatedCallCount++;
+
+            final long nowUptime = SystemClock.uptimeMillis();
+
+            // Was a slow call?
+            if (time >= slowThreshold) {
+                // We do a slow log:
+                // - At most once in every SLOW_LOG_INTERVAL_MS
+                // - OR it was slower than the previously logged slow call.
+                if ((nowUptime > (sLastSlowLogUptime + SLOW_LOG_INTERVAL_MS))
+                        || (sLastSlowLogActualTime < time)) {
+                    EventLogTags.writeServiceManagerSlow(time / 1000, name);
+
+                    sLastSlowLogUptime = nowUptime;
+                    sLastSlowLogActualTime = time;
+                }
+            }
+
+            // Every GET_SERVICE_LOG_EVERY_CALLS calls, log the total time spent in getService().
+
+            final int logInterval = isCore
+                    ? GET_SERVICE_LOG_EVERY_CALLS_CORE
+                    : GET_SERVICE_LOG_EVERY_CALLS_NON_CORE;
+
+            if ((sGetServiceAccumulatedCallCount >= logInterval)
+                    && (nowUptime >= (sLastStatsLogUptime + STATS_LOG_INTERVAL_MS))) {
+
+                EventLogTags.writeServiceManagerStats(
+                        sGetServiceAccumulatedCallCount, // Total # of getService() calls.
+                        sGetServiceAccumulatedUs / 1000, // Total time spent in getService() calls.
+                        (int) (nowUptime - sLastStatsLogUptime)); // Uptime duration since last log.
+                sGetServiceAccumulatedCallCount = 0;
+                sGetServiceAccumulatedUs = 0;
+                sLastStatsLogUptime = nowUptime;
+            }
+        }
+        return binder;
+    }
+}
diff --git a/android-34/android/os/ServiceManagerNative.java b/android-34/android/os/ServiceManagerNative.java
new file mode 100644
index 0000000..f2143f6
--- /dev/null
+++ b/android-34/android/os/ServiceManagerNative.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+/**
+ * Native implementation of the service manager.  Most clients will only
+ * care about asInterface().
+ *
+ * @hide
+ */
+public final class ServiceManagerNative {
+    private ServiceManagerNative() {}
+
+    /**
+     * Cast a Binder object into a service manager interface, generating
+     * a proxy if needed.
+     *
+     * TODO: delete this method and have clients use
+     *     IServiceManager.Stub.asInterface instead
+     */
+    @UnsupportedAppUsage
+    public static IServiceManager asInterface(IBinder obj) {
+        if (obj == null) {
+            return null;
+        }
+
+        // ServiceManager is never local
+        return new ServiceManagerProxy(obj);
+    }
+}
+
+// This class should be deleted and replaced with IServiceManager.Stub whenever
+// mRemote is no longer used
+class ServiceManagerProxy implements IServiceManager {
+    public ServiceManagerProxy(IBinder remote) {
+        mRemote = remote;
+        mServiceManager = IServiceManager.Stub.asInterface(remote);
+    }
+
+    public IBinder asBinder() {
+        return mRemote;
+    }
+
+    @UnsupportedAppUsage
+    public IBinder getService(String name) throws RemoteException {
+        // Same as checkService (old versions of servicemanager had both methods).
+        return mServiceManager.checkService(name);
+    }
+
+    public IBinder checkService(String name) throws RemoteException {
+        return mServiceManager.checkService(name);
+    }
+
+    public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
+            throws RemoteException {
+        mServiceManager.addService(name, service, allowIsolated, dumpPriority);
+    }
+
+    public String[] listServices(int dumpPriority) throws RemoteException {
+        return mServiceManager.listServices(dumpPriority);
+    }
+
+    public void registerForNotifications(String name, IServiceCallback cb)
+            throws RemoteException {
+        mServiceManager.registerForNotifications(name, cb);
+    }
+
+    public void unregisterForNotifications(String name, IServiceCallback cb)
+            throws RemoteException {
+        throw new RemoteException();
+    }
+
+    public boolean isDeclared(String name) throws RemoteException {
+        return mServiceManager.isDeclared(name);
+    }
+
+    public String[] getDeclaredInstances(String iface) throws RemoteException {
+        return mServiceManager.getDeclaredInstances(iface);
+    }
+
+    public String updatableViaApex(String name) throws RemoteException {
+        return mServiceManager.updatableViaApex(name);
+    }
+
+    public String[] getUpdatableNames(String apexName) throws RemoteException {
+        return mServiceManager.getUpdatableNames(apexName);
+    }
+
+    public ConnectionInfo getConnectionInfo(String name) throws RemoteException {
+        return mServiceManager.getConnectionInfo(name);
+    }
+
+    public void registerClientCallback(String name, IBinder service, IClientCallback cb)
+            throws RemoteException {
+        throw new RemoteException();
+    }
+
+    public void tryUnregisterService(String name, IBinder service) throws RemoteException {
+        throw new RemoteException();
+    }
+
+    public ServiceDebugInfo[] getServiceDebugInfo() throws RemoteException {
+        return mServiceManager.getServiceDebugInfo();
+    }
+
+    /**
+     * Same as mServiceManager but used by apps.
+     *
+     * Once this can be removed, ServiceManagerProxy should be removed entirely.
+     */
+    @UnsupportedAppUsage
+    private IBinder mRemote;
+
+    private IServiceManager mServiceManager;
+}
diff --git a/android-34/android/os/ServiceSpecificException.java b/android-34/android/os/ServiceSpecificException.java
new file mode 100644
index 0000000..49ce40b
--- /dev/null
+++ b/android-34/android/os/ServiceSpecificException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * An exception specific to a service.
+ *
+ * <p>This exception includes an error code specific to the throwing
+ * service.  This is mostly used by system services to indicate
+ * domain specific error conditions.</p>
+ *
+ * <p>Since these exceptions are designed to be passed through Binder
+ * interfaces, and to be generated by native-code Binder services,
+ * they do not support exception chaining.</p>
+ *
+ * @hide
+ */
+@SystemApi
+public class ServiceSpecificException extends RuntimeException {
+    public final int errorCode;
+
+    public ServiceSpecificException(int errorCode, @Nullable String message) {
+        super(message);
+        this.errorCode = errorCode;
+    }
+
+    public ServiceSpecificException(int errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return super.toString() + " (code " + errorCode + ")";
+    }
+}
diff --git a/android-34/android/os/SharedMemory.java b/android-34/android/os/SharedMemory.java
new file mode 100644
index 0000000..cba4423
--- /dev/null
+++ b/android-34/android/os/SharedMemory.java
@@ -0,0 +1,403 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import dalvik.system.VMRuntime;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.DirectByteBuffer;
+import java.nio.NioUtils;
+
+import sun.misc.Cleaner;
+
+/**
+ * SharedMemory enables the creation, mapping, and protection control over anonymous shared memory.
+ */
+public final class SharedMemory implements Parcelable, Closeable {
+
+    private final FileDescriptor mFileDescriptor;
+    private final int mSize;
+    private final MemoryRegistration mMemoryRegistration;
+    private Cleaner mCleaner;
+
+    private SharedMemory(FileDescriptor fd) {
+        // This constructor is only used internally so it should be impossible to hit any of the
+        // exceptions unless something goes horribly wrong.
+        if (fd == null) {
+            throw new IllegalArgumentException(
+                    "Unable to create SharedMemory from a null FileDescriptor");
+        }
+        if (!fd.valid()) {
+            throw new IllegalArgumentException(
+                    "Unable to create SharedMemory from closed FileDescriptor");
+        }
+        mFileDescriptor = fd;
+        mSize = nGetSize(mFileDescriptor);
+        if (mSize <= 0) {
+            throw new IllegalArgumentException("FileDescriptor is not a valid ashmem fd");
+        }
+
+        mMemoryRegistration = new MemoryRegistration(mSize);
+        mCleaner = Cleaner.create(mFileDescriptor,
+                new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
+    }
+
+    /**
+     * Creates an anonymous SharedMemory instance with the provided debug name and size. The name
+     * is only used for debugging purposes and can help identify what the shared memory is used
+     * for when inspecting memory maps for the processes that have mapped this SharedMemory
+     * instance.
+     *
+     * @param name The debug name to use for this SharedMemory instance. This can be null, however
+     *             a debug name is recommended to help identify memory usage when using tools
+     *             such as lsof or examining /proc/[pid]/maps
+     * @param size The size of the shared memory to create. Must be greater than 0.
+     * @return A SharedMemory instance of the requested size
+     * @throws ErrnoException if the requested allocation fails.
+     */
+    public static @NonNull SharedMemory create(@Nullable String name, int size)
+            throws ErrnoException {
+        if (size <= 0) {
+            throw new IllegalArgumentException("Size must be greater than zero");
+        }
+        return new SharedMemory(nCreate(name, size));
+    }
+
+    private void checkOpen() {
+        if (!mFileDescriptor.valid()) {
+            throw new IllegalStateException("SharedMemory is closed");
+        }
+    }
+
+    /**
+     * Creates an instance from existing shared memory passed as {@link ParcelFileDescriptor}.
+     *
+     * <p> The {@code fd} should be a shared memory created from
+       {@code SharedMemory or ASharedMemory}. This can be useful when shared memory is passed as
+       file descriptor through JNI or binder service implemented in cpp.
+     * <p> Note that newly created {@code SharedMemory} takes ownership of passed {@code fd} and
+     * the original {@code fd} becomes detached (Check {@link ParcelFileDescriptor#detachFd()}).
+     * If the caller wants to use the file descriptor after the call, the caller should duplicate
+     * the file descriptor (Check {@link ParcelFileDescriptor#dup()}) and pass the duped version
+     * instead.
+     *
+     * @param fd File descriptor of shared memory passed as {@link ParcelFileDescriptor}.
+     */
+    public static @NonNull SharedMemory fromFileDescriptor(@NonNull ParcelFileDescriptor fd) {
+        FileDescriptor f = new FileDescriptor();
+        f.setInt$(fd.detachFd());
+        return new SharedMemory(f);
+    }
+
+    private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE
+            | OsConstants.PROT_EXEC | OsConstants.PROT_NONE;
+
+    private static void validateProt(int prot) {
+        if ((prot & ~PROT_MASK) != 0) {
+            throw new IllegalArgumentException("Invalid prot value");
+        }
+    }
+
+    /**
+     * Sets the protection on the shared memory to the combination specified in prot, which
+     * is either a bitwise-or'd combination of {@link android.system.OsConstants#PROT_READ},
+     * {@link android.system.OsConstants#PROT_WRITE}, {@link android.system.OsConstants#PROT_EXEC}
+     * from {@link android.system.OsConstants}, or {@link android.system.OsConstants#PROT_NONE},
+     * to remove all further access.
+     *
+     * Note that protection can only ever be removed, not added. By default shared memory
+     * is created with protection set to PROT_READ | PROT_WRITE | PROT_EXEC. The protection
+     * passed here also only applies to any mappings created after calling this method. Existing
+     * mmaps of the shared memory retain whatever protection they had when they were created.
+     *
+     * A common usage of this is to share a read-only copy of the data with something else. To do
+     * that first create the read/write mapping with PROT_READ | PROT_WRITE,
+     * then call setProtect(PROT_READ) to remove write capability, then send the SharedMemory
+     * to another process. That process will only be able to mmap with PROT_READ.
+     *
+     * @param prot Any bitwise-or'ed combination of
+     *                  {@link android.system.OsConstants#PROT_READ},
+     *                  {@link android.system.OsConstants#PROT_WRITE}, and
+     *                  {@link android.system.OsConstants#PROT_EXEC}; or
+     *                  {@link android.system.OsConstants#PROT_NONE}
+     * @return Whether or not the requested protection was applied. Returns true on success,
+     * false if the requested protection was broader than the existing protection.
+     */
+    public boolean setProtect(int prot) {
+        checkOpen();
+        validateProt(prot);
+        int errno = nSetProt(mFileDescriptor, prot);
+        return errno == 0;
+    }
+
+    /**
+     * Returns the backing {@link FileDescriptor} for this SharedMemory object. The SharedMemory
+     * instance retains ownership of the FileDescriptor.
+     *
+     * This FileDescriptor is interoperable with the ASharedMemory NDK APIs.
+     *
+     * @return Returns the FileDescriptor associated with this object.
+     *
+     * @hide Exists only for MemoryFile interop
+     */
+    public @NonNull FileDescriptor getFileDescriptor() {
+        return mFileDescriptor;
+    }
+
+    /**
+     * Returns the backing native fd int for this SharedMemory object. The SharedMemory
+     * instance retains ownership of the fd.
+     *
+     * This fd is interoperable with the ASharedMemory NDK APIs.
+     *
+     * @return Returns the native fd associated with this object, or -1 if it is already closed.
+     *
+     * @hide Exposed for native ASharedMemory_dupFromJava()
+     */
+    @UnsupportedAppUsage(trackingBug = 171971817)
+    public int getFd() {
+        return mFileDescriptor.getInt$();
+    }
+
+    /**
+     * @return The size of the SharedMemory region.
+     */
+    public int getSize() {
+        checkOpen();
+        return mSize;
+    }
+
+    /**
+     * Creates a read/write mapping of the entire shared memory region. This requires the the
+     * protection level of the shared memory is at least PROT_READ|PROT_WRITE or the map will fail.
+     *
+     * Use {@link #map(int, int, int)} to have more control over the mapping if desired.
+     * This is equivalent to map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, getSize())
+     *
+     * @return A ByteBuffer mapping
+     * @throws ErrnoException if the mmap call failed.
+     */
+    public @NonNull ByteBuffer mapReadWrite() throws ErrnoException {
+        return map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, mSize);
+    }
+
+    /**
+     * Creates a read-only mapping of the entire shared memory region. This requires the the
+     * protection level of the shared memory is at least PROT_READ or the map will fail.
+     *
+     * Use {@link #map(int, int, int)} to have more control over the mapping if desired.
+     * This is equivalent to map(OsConstants.PROT_READ, 0, getSize())
+     *
+     * @return A ByteBuffer mapping
+     * @throws ErrnoException if the mmap call failed.
+     */
+    public @NonNull ByteBuffer mapReadOnly() throws ErrnoException {
+        return map(OsConstants.PROT_READ, 0, mSize);
+    }
+
+    /**
+     * Creates an mmap of the SharedMemory with the specified prot, offset, and length. This will
+     * always produce a new ByteBuffer window to the backing shared memory region. Every call
+     * to map() may be paired with a call to {@link #unmap(ByteBuffer)} when the ByteBuffer
+     * returned by map() is no longer needed.
+     *
+     * @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE.
+     * @param offset The offset into the shared memory to begin mapping. Must be >= 0 and less than
+     *         getSize().
+     * @param length The length of the region to map. Must be > 0 and offset + length must not
+     *         exceed getSize().
+     * @return A ByteBuffer mapping.
+     * @throws ErrnoException if the mmap call failed.
+     */
+    public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException {
+        checkOpen();
+        validateProt(prot);
+        if (offset < 0) {
+            throw new IllegalArgumentException("Offset must be >= 0");
+        }
+        if (length <= 0) {
+            throw new IllegalArgumentException("Length must be > 0");
+        }
+        if (offset + length > mSize) {
+            throw new IllegalArgumentException("offset + length must not exceed getSize()");
+        }
+        long address = Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset);
+        boolean readOnly = (prot & OsConstants.PROT_WRITE) == 0;
+        Runnable unmapper = new Unmapper(address, length, mMemoryRegistration.acquire());
+        return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly);
+    }
+
+    /**
+     * Unmaps a buffer previously returned by {@link #map(int, int, int)}. This will immediately
+     * release the backing memory of the ByteBuffer, invalidating all references to it. Only
+     * call this method if there are no duplicates of the ByteBuffer in use and don't
+     * access the ByteBuffer after calling this method.
+     *
+     * @param buffer The buffer to unmap
+     */
+    public static void unmap(@NonNull ByteBuffer buffer) {
+        if (buffer instanceof DirectByteBuffer) {
+            NioUtils.freeDirectBuffer(buffer);
+        } else {
+            throw new IllegalArgumentException(
+                    "ByteBuffer wasn't created by #map(int, int, int); can't unmap");
+        }
+    }
+
+    /**
+     * Close the backing {@link FileDescriptor} of this SharedMemory instance. Note that all
+     * open mappings of the shared memory will remain valid and may continue to be used. The
+     * shared memory will not be freed until all file descriptor handles are closed and all
+     * memory mappings are unmapped.
+     */
+    @Override
+    public void close() {
+        mFileDescriptor.setInt$(-1);
+        if (mCleaner != null) {
+            mCleaner.clean();
+            mCleaner = null;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return CONTENTS_FILE_DESCRIPTOR;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        checkOpen();
+        dest.writeFileDescriptor(mFileDescriptor);
+    }
+
+    /**
+     * Returns a dup'd ParcelFileDescriptor from the SharedMemory FileDescriptor.
+     * This obeys standard POSIX semantics, where the
+     * new file descriptor shared state such as file position with the
+     * original file descriptor.
+     * TODO: propose this method as a public or system API for next release to achieve parity with
+     *  NDK ASharedMemory_dupFromJava.
+     *
+     * @hide
+     */
+    public ParcelFileDescriptor getFdDup() throws IOException {
+        return ParcelFileDescriptor.dup(mFileDescriptor);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<SharedMemory> CREATOR =
+            new Parcelable.Creator<SharedMemory>() {
+        @Override
+        public SharedMemory createFromParcel(Parcel source) {
+            FileDescriptor descriptor = source.readRawFileDescriptor();
+            return new SharedMemory(descriptor);
+        }
+
+        @Override
+        public SharedMemory[] newArray(int size) {
+            return new SharedMemory[size];
+        }
+    };
+
+    /**
+     * Cleaner that closes the FD
+     */
+    private static final class Closer implements Runnable {
+        private int mFd;
+        private MemoryRegistration mMemoryReference;
+
+        private Closer(int fd, MemoryRegistration memoryReference) {
+            mFd = fd;
+            mMemoryReference = memoryReference;
+        }
+
+        @Override
+        public void run() {
+            try {
+                FileDescriptor fd = new FileDescriptor();
+                fd.setInt$(mFd);
+                Os.close(fd);
+            } catch (ErrnoException e) { /* swallow error */ }
+            mMemoryReference.release();
+            mMemoryReference = null;
+        }
+    }
+
+    /**
+     * Cleaner that munmap regions
+     */
+    private static final class Unmapper implements Runnable {
+        private long mAddress;
+        private int mSize;
+        private MemoryRegistration mMemoryReference;
+
+        private Unmapper(long address, int size, MemoryRegistration memoryReference) {
+            mAddress = address;
+            mSize = size;
+            mMemoryReference = memoryReference;
+        }
+
+        @Override
+        public void run() {
+            try {
+                Os.munmap(mAddress, mSize);
+            } catch (ErrnoException e) { /* swallow exception */ }
+            mMemoryReference.release();
+            mMemoryReference = null;
+        }
+    }
+
+    /**
+     * Helper class that ensures that the native allocation pressure against the VM heap stays
+     * active until the FD is closed as well as all mappings from that FD are closed.
+     */
+    private static final class MemoryRegistration {
+        private int mSize;
+        private int mReferenceCount;
+
+        private MemoryRegistration(int size) {
+            mSize = size;
+            mReferenceCount = 1;
+            VMRuntime.getRuntime().registerNativeAllocation(mSize);
+        }
+
+        public synchronized MemoryRegistration acquire() {
+            mReferenceCount++;
+            return this;
+        }
+
+        public synchronized void release() {
+            mReferenceCount--;
+            if (mReferenceCount == 0) {
+                VMRuntime.getRuntime().registerNativeFree(mSize);
+            }
+        }
+    }
+
+    private static native FileDescriptor nCreate(String name, int size) throws ErrnoException;
+    private static native int nGetSize(FileDescriptor fd);
+    private static native int nSetProt(FileDescriptor fd, int prot);
+}
diff --git a/android-34/android/os/SharedPreferencesTest.java b/android-34/android/os/SharedPreferencesTest.java
new file mode 100644
index 0000000..dd479ac
--- /dev/null
+++ b/android-34/android/os/SharedPreferencesTest.java
@@ -0,0 +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 android.os;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SharedPreferencesTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeCachedGetSharedPreferences() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final Context context = InstrumentationRegistry.getTargetContext();
+        // Do the real work once as we're only interested in cache-hit performance
+        SharedPreferences prefs = context.getSharedPreferences("test", Context.MODE_PRIVATE);
+        while (state.keepRunning()) {
+            prefs = context.getSharedPreferences("test", Context.MODE_PRIVATE);
+        }
+    }
+}
diff --git a/android-34/android/os/ShellCallback.java b/android-34/android/os/ShellCallback.java
new file mode 100644
index 0000000..be9fb89
--- /dev/null
+++ b/android-34/android/os/ShellCallback.java
@@ -0,0 +1,126 @@
+/*
+ * 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.os;
+
+import android.util.Log;
+
+import com.android.internal.os.IShellCallback;
+
+/**
+ * Special-purpose API for use with {@link IBinder#shellCommand IBinder.shellCommand} for
+ * performing operations back on the invoking shell.
+ * @hide
+ */
+public class ShellCallback implements Parcelable {
+    final static String TAG = "ShellCallback";
+
+    final static boolean DEBUG = false;
+
+    final boolean mLocal;
+
+    IShellCallback mShellCallback;
+
+    class MyShellCallback extends IShellCallback.Stub {
+        public ParcelFileDescriptor openFile(String path, String seLinuxContext,
+                String mode) {
+            return onOpenFile(path, seLinuxContext, mode);
+        }
+    }
+
+    /**
+     * Create a new ShellCallback to receive requests.
+     */
+    public ShellCallback() {
+        mLocal = true;
+    }
+
+    /**
+     * Ask the shell to open a file.  If opening for writing, will truncate the file if it
+     * already exists and will create the file if it doesn't exist.
+     * @param path Path of the file to be opened/created.
+     * @param seLinuxContext Optional SELinux context that must be allowed to have
+     * access to the file; if null, nothing is required.
+     * @param mode Mode to open file in: "r" for input/reading an existing file,
+     * "r+" for reading/writing an existing file, "w" for output/writing a new file (either
+     * creating or truncating an existing one), "w+" for reading/writing a new file (either
+     * creating or truncating an existing one).
+     */
+    public ParcelFileDescriptor openFile(String path, String seLinuxContext, String mode) {
+        if (DEBUG) Log.d(TAG, "openFile " + this + " mode=" + mode + ": mLocal=" + mLocal
+                + " mShellCallback=" + mShellCallback);
+
+        if (mLocal) {
+            return onOpenFile(path, seLinuxContext, mode);
+        }
+
+        if (mShellCallback != null) {
+            try {
+                return mShellCallback.openFile(path, seLinuxContext, mode);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failure opening " + path, e);
+            }
+        }
+        return null;
+    }
+
+    public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext, String mode) {
+        return null;
+    }
+
+    public static void writeToParcel(ShellCallback callback, Parcel out) {
+        if (callback == null) {
+            out.writeStrongBinder(null);
+        } else {
+            callback.writeToParcel(out, 0);
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        synchronized (this) {
+            if (mShellCallback == null) {
+                mShellCallback = new MyShellCallback();
+            }
+            out.writeStrongBinder(mShellCallback.asBinder());
+        }
+    }
+
+    public IBinder getShellCallbackBinder() {
+        return mShellCallback.asBinder();
+    }
+
+    ShellCallback(Parcel in) {
+        mLocal = false;
+        mShellCallback = IShellCallback.Stub.asInterface(in.readStrongBinder());
+        if (mShellCallback != null) {
+            Binder.allowBlocking(mShellCallback.asBinder());
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ShellCallback> CREATOR
+            = new Parcelable.Creator<ShellCallback>() {
+        public ShellCallback createFromParcel(Parcel in) {
+            return new ShellCallback(in);
+        }
+        public ShellCallback[] newArray(int size) {
+            return new ShellCallback[size];
+        }
+    };
+}
diff --git a/android-34/android/os/ShellCommand.java b/android-34/android/os/ShellCommand.java
new file mode 100644
index 0000000..a2173a6
--- /dev/null
+++ b/android-34/android/os/ShellCommand.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Slog;
+
+import com.android.modules.utils.BasicShellCommandHandler;
+
+import java.io.FileDescriptor;
+
+/**
+ * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}.
+ * @hide
+ */
+public abstract class ShellCommand extends BasicShellCommandHandler {
+    private ShellCallback mShellCallback;
+    private ResultReceiver mResultReceiver;
+
+    public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+        mShellCallback = callback;
+        mResultReceiver = resultReceiver;
+        final int result = super.exec(target, in, out, err, args);
+
+        if (mResultReceiver != null) {
+            mResultReceiver.send(result, null);
+        }
+
+        return result;
+    }
+
+    /**
+     * Adopt the ResultReceiver that was given to this shell command from it, taking
+     * it over.  Primarily used to dispatch to another shell command.  Once called,
+     * this shell command will no longer return its own result when done.
+     */
+    public ResultReceiver adoptResultReceiver() {
+        ResultReceiver rr = mResultReceiver;
+        mResultReceiver = null;
+        return rr;
+    }
+
+    /**
+     * Helper for just system services to ask the shell to open an output file.
+     * @hide
+     */
+    public ParcelFileDescriptor openFileForSystem(String path, String mode) {
+        if (DEBUG) Slog.d(TAG, "openFileForSystem: " + path + " mode=" + mode);
+        try {
+            ParcelFileDescriptor pfd = getShellCallback().openFile(path,
+                    "u:r:system_server:s0", mode);
+            if (pfd != null) {
+                if (DEBUG) Slog.d(TAG, "Got file: " + pfd);
+                return pfd;
+            }
+        } catch (RuntimeException e) {
+            if (DEBUG) Slog.d(TAG, "Failure opening file: " + e.getMessage());
+            getErrPrintWriter().println("Failure opening file: " + e.getMessage());
+        }
+        if (DEBUG) Slog.d(TAG, "Error: Unable to open file: " + path);
+        getErrPrintWriter().println("Error: Unable to open file: " + path);
+
+        String suggestedPath = "/data/local/tmp/";
+        if (path == null || !path.startsWith(suggestedPath)) {
+            getErrPrintWriter().println("Consider using a file under " + suggestedPath);
+        }
+        return null;
+    }
+
+    public int handleDefaultCommands(String cmd) {
+        if ("dump".equals(cmd)) {
+            String[] newArgs = new String[getAllArgs().length-1];
+            System.arraycopy(getAllArgs(), 1, newArgs, 0, getAllArgs().length-1);
+            getTarget().doDump(getOutFileDescriptor(), getOutPrintWriter(), newArgs);
+            return 0;
+        }
+        return super.handleDefaultCommands(cmd);
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public String peekNextArg() {
+        return super.peekNextArg();
+    }
+
+    /**
+     * Return the {@link ShellCallback} for communicating back with the calling shell.
+     */
+    public ShellCallback getShellCallback() {
+        return mShellCallback;
+    }
+}
diff --git a/android-34/android/os/SimpleClock.java b/android-34/android/os/SimpleClock.java
new file mode 100644
index 0000000..efc271f
--- /dev/null
+++ b/android-34/android/os/SimpleClock.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+
+/** {@hide} */
+public abstract class SimpleClock extends Clock {
+    private final ZoneId zone;
+
+    public SimpleClock(ZoneId zone) {
+        this.zone = zone;
+    }
+
+    @Override
+    public ZoneId getZone() {
+        return zone;
+    }
+
+    @Override
+    public Clock withZone(ZoneId zone) {
+        return new SimpleClock(zone) {
+            @Override
+            public long millis() {
+                return SimpleClock.this.millis();
+            }
+        };
+    }
+
+    @Override
+    public abstract long millis();
+
+    @Override
+    public Instant instant() {
+        return Instant.ofEpochMilli(millis());
+    }
+}
diff --git a/android-34/android/os/SomeProvider.java b/android-34/android/os/SomeProvider.java
new file mode 100644
index 0000000..f5e247e
--- /dev/null
+++ b/android-34/android/os/SomeProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+import java.util.Arrays;
+
+public class SomeProvider extends ContentProvider {
+    private Cursor mCursor;
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return mCursor;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        final char[] valueRaw = new char[512];
+        Arrays.fill(valueRaw, '!');
+        final String value = new String(valueRaw);
+
+        final int count = values.getAsInteger(Intent.EXTRA_INDEX);
+        final MatrixCursor cursor = new MatrixCursor(new String[] { "_id", "value" });
+        for (int i = 0; i < count; i++) {
+            MatrixCursor.RowBuilder row = cursor.newRow();
+            row.add(0, i);
+            row.add(1, value);
+        }
+        mCursor = cursor;
+        return 1;
+    }
+}
diff --git a/android-34/android/os/SomeService.java b/android-34/android/os/SomeService.java
new file mode 100644
index 0000000..bdfaa44
--- /dev/null
+++ b/android-34/android/os/SomeService.java
@@ -0,0 +1,42 @@
+package android.os;
+
+import android.app.Service;
+import android.content.Intent;
+import java.io.File;
+import java.io.IOException;
+
+/** Service in separate process available for calling over binder. */
+public class SomeService extends Service {
+
+    private File mTempFile;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        try {
+            mTempFile = File.createTempFile("foo", "bar");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private final ISomeService.Stub mBinder =
+            new ISomeService.Stub() {
+                public void readDisk(int times) {
+                    for (int i = 0; i < times; i++) {
+                        mTempFile.exists();
+                    }
+                }
+            };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mTempFile.delete();
+    }
+}
diff --git a/android-34/android/os/StatFs.java b/android-34/android/os/StatFs.java
new file mode 100644
index 0000000..bb6066e
--- /dev/null
+++ b/android-34/android/os/StatFs.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStatVfs;
+
+/**
+ * Retrieve overall information about the space on a filesystem. This is a
+ * wrapper for Unix statvfs().
+ */
+public class StatFs {
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private StructStatVfs mStat;
+
+    /**
+     * Construct a new StatFs for looking at the stats of the filesystem at
+     * {@code path}. Upon construction, the stat of the file system will be
+     * performed, and the values retrieved available from the methods on this
+     * class.
+     *
+     * @param path path in the desired file system to stat.
+     *
+     * @throws IllegalArgumentException if the file system access fails
+     */
+    public StatFs(String path) {
+        mStat = doStat(path);
+    }
+
+    /**
+     * @throws IllegalArgumentException if the file system access fails
+     */
+    private static StructStatVfs doStat(String path) {
+        try {
+            return Os.statvfs(path);
+        } catch (ErrnoException e) {
+            throw new IllegalArgumentException("Invalid path: " + path, e);
+        }
+    }
+
+    /**
+     * Perform a restat of the file system referenced by this object. This is
+     * the same as re-constructing the object with the same file system path,
+     * and the new stat values are available upon return.
+     *
+     * @throws IllegalArgumentException if the file system access fails
+     */
+    public void restat(String path) {
+        mStat = doStat(path);
+    }
+
+    /**
+     * @deprecated Use {@link #getBlockSizeLong()} instead.
+     */
+    @Deprecated
+    public int getBlockSize() {
+        return (int) mStat.f_frsize;
+    }
+
+    /**
+     * The size, in bytes, of a block on the file system. This corresponds to
+     * the Unix {@code statvfs.f_frsize} field.
+     */
+    public long getBlockSizeLong() {
+        return mStat.f_frsize;
+    }
+
+    /**
+     * @deprecated Use {@link #getBlockCountLong()} instead.
+     */
+    @Deprecated
+    public int getBlockCount() {
+        return (int) mStat.f_blocks;
+    }
+
+    /**
+     * The total number of blocks on the file system. This corresponds to the
+     * Unix {@code statvfs.f_blocks} field.
+     */
+    public long getBlockCountLong() {
+        return mStat.f_blocks;
+    }
+
+    /**
+     * @deprecated Use {@link #getFreeBlocksLong()} instead.
+     */
+    @Deprecated
+    public int getFreeBlocks() {
+        return (int) mStat.f_bfree;
+    }
+
+    /**
+     * The total number of blocks that are free on the file system, including
+     * reserved blocks (that are not available to normal applications). This
+     * corresponds to the Unix {@code statvfs.f_bfree} field. Most applications
+     * will want to use {@link #getAvailableBlocksLong()} instead.
+     */
+    public long getFreeBlocksLong() {
+        return mStat.f_bfree;
+    }
+
+    /**
+     * The number of bytes that are free on the file system, including reserved
+     * blocks (that are not available to normal applications). Most applications
+     * will want to use {@link #getAvailableBytes()} instead.
+     */
+    public long getFreeBytes() {
+        return mStat.f_bfree * mStat.f_frsize;
+    }
+
+    /**
+     * @deprecated Use {@link #getAvailableBlocksLong()} instead.
+     */
+    @Deprecated
+    public int getAvailableBlocks() {
+        return (int) mStat.f_bavail;
+    }
+
+    /**
+     * The number of blocks that are free on the file system and available to
+     * applications. This corresponds to the Unix {@code statvfs.f_bavail} field.
+     */
+    public long getAvailableBlocksLong() {
+        return mStat.f_bavail;
+    }
+
+    /**
+     * The number of bytes that are free on the file system and available to
+     * applications.
+     */
+    public long getAvailableBytes() {
+        return mStat.f_bavail * mStat.f_frsize;
+    }
+
+    /**
+     * The total number of bytes supported by the file system.
+     */
+    public long getTotalBytes() {
+        return mStat.f_blocks * mStat.f_frsize;
+    }
+}
diff --git a/android-34/android/os/StatsServiceManager.java b/android-34/android/os/StatsServiceManager.java
new file mode 100644
index 0000000..de07e92
--- /dev/null
+++ b/android-34/android/os/StatsServiceManager.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the stats
+ * service.
+ *
+ * <p> Only the statsd mainline module will be able to access an instance of this class.
+ * @hide
+ */
+@SystemApi(client = Client.MODULE_LIBRARIES)
+public class StatsServiceManager {
+    /**
+     * @hide
+     */
+    public StatsServiceManager() {}
+
+    /**
+     * A class that exposes the methods to register and obtain each system service.
+     */
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Get the system server binding object for StatsManagerService.
+         *
+         * <p> This blocks until the service instance is ready.
+         * or a timeout happens, in which case it returns null.
+         */
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+         */
+        @Nullable
+        public IBinder getOrThrow() throws ServiceNotFoundException {
+            try {
+                return ServiceManager.getServiceOrThrow(mServiceName);
+            } catch (ServiceManager.ServiceNotFoundException e) {
+                throw new ServiceNotFoundException(mServiceName);
+            }
+        }
+
+        /**
+         * Get the system server binding object for a service. If the specified service is
+         * not available, it returns null.
+         */
+        @Nullable
+        private IBinder tryGet() {
+            return ServiceManager.checkService(mServiceName);
+        }
+    }
+
+    /**
+     * See {@link ServiceRegisterer#getOrThrow()}
+     */
+    public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+        /**
+         * Constructor
+         *
+         * @param name the name of the binder service that cannot be found.
+         */
+        public ServiceNotFoundException(@NonNull String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "statscompanion" service.
+     */
+    @NonNull
+    public ServiceRegisterer getStatsCompanionServiceRegisterer() {
+        return new ServiceRegisterer("statscompanion");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "statsmanager" service.
+     */
+    @NonNull
+    public ServiceRegisterer getStatsManagerServiceRegisterer() {
+        return new ServiceRegisterer("statsmanager");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "statsd" service.
+     */
+    @NonNull
+    public ServiceRegisterer getStatsdServiceRegisterer() {
+        return new ServiceRegisterer("stats");
+    }
+}
diff --git a/android-34/android/os/StrictMode.java b/android-34/android/os/StrictMode.java
new file mode 100644
index 0000000..180735b
--- /dev/null
+++ b/android-34/android/os/StrictMode.java
@@ -0,0 +1,3202 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.IActivityManager;
+import android.app.IUnsafeIntentStrictModeCallback;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.os.storage.IStorageManager;
+import android.os.strictmode.CleartextNetworkViolation;
+import android.os.strictmode.ContentUriWithoutPermissionViolation;
+import android.os.strictmode.CredentialProtectedWhileLockedViolation;
+import android.os.strictmode.CustomViolation;
+import android.os.strictmode.DiskReadViolation;
+import android.os.strictmode.DiskWriteViolation;
+import android.os.strictmode.ExplicitGcViolation;
+import android.os.strictmode.FileUriExposedViolation;
+import android.os.strictmode.ImplicitDirectBootViolation;
+import android.os.strictmode.IncorrectContextUseViolation;
+import android.os.strictmode.InstanceCountViolation;
+import android.os.strictmode.IntentReceiverLeakedViolation;
+import android.os.strictmode.LeakedClosableViolation;
+import android.os.strictmode.NetworkViolation;
+import android.os.strictmode.NonSdkApiUsedViolation;
+import android.os.strictmode.ResourceMismatchViolation;
+import android.os.strictmode.ServiceConnectionLeakedViolation;
+import android.os.strictmode.SqliteObjectLeakedViolation;
+import android.os.strictmode.UnbufferedIoViolation;
+import android.os.strictmode.UnsafeIntentLaunchViolation;
+import android.os.strictmode.UntaggedSocketViolation;
+import android.os.strictmode.Violation;
+import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Printer;
+import android.util.Singleton;
+import android.util.Slog;
+import android.util.SparseLongArray;
+import android.view.IWindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.RuntimeInit;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.HexDump;
+
+import dalvik.system.BlockGuard;
+import dalvik.system.CloseGuard;
+import dalvik.system.VMDebug;
+import dalvik.system.VMRuntime;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+/**
+ * StrictMode is a developer tool which detects things you might be doing by accident and brings
+ * them to your attention so you can fix them.
+ *
+ * <p>StrictMode is most commonly used to catch accidental disk or network access on the
+ * application's main thread, where UI operations are received and animations take place. Keeping
+ * disk and network operations off the main thread makes for much smoother, more responsive
+ * applications. By keeping your application's main thread responsive, you also prevent <a
+ * href="{@docRoot}guide/practices/design/responsiveness.html">ANR dialogs</a> from being shown to
+ * users.
+ *
+ * <p class="note">Note that even though an Android device's disk is often on flash memory, many
+ * devices run a filesystem on top of that memory with very limited concurrency. It's often the case
+ * that almost all disk accesses are fast, but may in individual cases be dramatically slower when
+ * certain I/O is happening in the background from other processes. If possible, it's best to assume
+ * that such things are not fast.
+ *
+ * <p>Example code to enable from early in your {@link android.app.Application}, {@link
+ * android.app.Activity}, or other application component's {@link android.app.Application#onCreate}
+ * method:
+ *
+ * <pre>
+ * public void onCreate() {
+ *     StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
+ *             .detectDiskReads()
+ *             .detectDiskWrites()
+ *             .detectNetwork()   // or .detectAll() for all detectable problems
+ *             .penaltyLog()
+ *             .build());
+ *     StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
+ *             .detectLeakedSqlLiteObjects()
+ *             .detectLeakedClosableObjects()
+ *             .penaltyLog()
+ *             .penaltyDeath()
+ *             .build());
+ *     super.onCreate();
+ * }
+ * </pre>
+ *
+ * <p>You can decide what should happen when a violation is detected. For example, using {@link
+ * ThreadPolicy.Builder#penaltyLog} you can watch the output of <code>adb logcat</code> while you
+ * use your application to see the violations as they happen.
+ *
+ * <p>If you find violations that you feel are problematic, there are a variety of tools to help
+ * solve them: threads, {@link android.os.Handler}, {@link android.os.AsyncTask}, {@link
+ * android.app.IntentService}, etc. But don't feel compelled to fix everything that StrictMode
+ * finds. In particular, many cases of disk access are often necessary during the normal activity
+ * lifecycle. Use StrictMode to find things you did by accident. Network requests on the UI thread
+ * are almost always a problem, though.
+ *
+ * <p class="note">StrictMode is not a security mechanism and is not guaranteed to find all disk or
+ * network accesses. While it does propagate its state across process boundaries when doing {@link
+ * android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network
+ * access from JNI calls won't necessarily trigger it.
+ */
+public final class StrictMode {
+    private static final String TAG = "StrictMode";
+    private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * Boolean system property to disable strict mode checks outright. Set this to 'true' to force
+     * disable; 'false' has no effect on other enable/disable policy.
+     *
+     * @hide
+     */
+    public static final String DISABLE_PROPERTY = "persist.sys.strictmode.disable";
+
+    /**
+     * The boolean system property to control screen flashes on violations.
+     *
+     * @hide
+     */
+    public static final String VISUAL_PROPERTY = "persist.sys.strictmode.visual";
+
+    /**
+     * Temporary property used to include {@link #DETECT_VM_CLEARTEXT_NETWORK} in {@link
+     * VmPolicy.Builder#detectAll()}. Apps can still always opt-into detection using {@link
+     * VmPolicy.Builder#detectCleartextNetwork()}.
+     */
+    private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear";
+
+    /**
+     * Quick feature-flag that can be used to disable the defaults provided by {@link
+     * #initThreadDefaults(ApplicationInfo)} and {@link #initVmDefaults(ApplicationInfo)}.
+     */
+    private static final boolean DISABLE = false;
+
+    // Only apply VM penalties for the same violation at this interval.
+    private static final long MIN_VM_INTERVAL_MS = 1000;
+
+    // Only log a duplicate stack trace to the logs every second.
+    private static final long MIN_LOG_INTERVAL_MS = 1000;
+
+    // Only show an annoying dialog at most every 30 seconds
+    private static final long MIN_DIALOG_INTERVAL_MS = 30000;
+
+    // Only log a dropbox entry at most every 30 seconds
+    private static final long MIN_DROPBOX_INTERVAL_MS = 3000;
+
+    // How many Span tags (e.g. animations) to report.
+    private static final int MAX_SPAN_TAGS = 20;
+
+    // How many offending stacks to keep track of (and time) per loop
+    // of the Looper.
+    private static final int MAX_OFFENSES_PER_LOOP = 10;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = {
+            DETECT_THREAD_DISK_WRITE,
+            DETECT_THREAD_DISK_READ,
+            DETECT_THREAD_NETWORK,
+            DETECT_THREAD_CUSTOM,
+            DETECT_THREAD_RESOURCE_MISMATCH,
+            DETECT_THREAD_UNBUFFERED_IO,
+            DETECT_THREAD_EXPLICIT_GC,
+            PENALTY_GATHER,
+            PENALTY_LOG,
+            PENALTY_DIALOG,
+            PENALTY_DEATH,
+            PENALTY_FLASH,
+            PENALTY_DROPBOX,
+            PENALTY_DEATH_ON_NETWORK,
+            PENALTY_DEATH_ON_CLEARTEXT_NETWORK,
+            PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ThreadPolicyMask {}
+
+    // Thread policy: bits 0-15
+
+    /** @hide */
+    private static final int DETECT_THREAD_DISK_WRITE = 1 << 0;
+    /** @hide */
+    private static final int DETECT_THREAD_DISK_READ = 1 << 1;
+    /** @hide */
+    private static final int DETECT_THREAD_NETWORK = 1 << 2;
+    /** @hide */
+    private static final int DETECT_THREAD_CUSTOM = 1 << 3;
+    /** @hide */
+    private static final int DETECT_THREAD_RESOURCE_MISMATCH = 1 << 4;
+    /** @hide */
+    private static final int DETECT_THREAD_UNBUFFERED_IO = 1 << 5;
+    /** @hide  */
+    private static final int DETECT_THREAD_EXPLICIT_GC = 1 << 6;
+
+    /** @hide */
+    private static final int DETECT_THREAD_ALL = 0x0000ffff;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = {
+            DETECT_VM_CURSOR_LEAKS,
+            DETECT_VM_CLOSABLE_LEAKS,
+            DETECT_VM_ACTIVITY_LEAKS,
+            DETECT_VM_INSTANCE_LEAKS,
+            DETECT_VM_REGISTRATION_LEAKS,
+            DETECT_VM_FILE_URI_EXPOSURE,
+            DETECT_VM_CLEARTEXT_NETWORK,
+            DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION,
+            DETECT_VM_UNTAGGED_SOCKET,
+            DETECT_VM_NON_SDK_API_USAGE,
+            DETECT_VM_IMPLICIT_DIRECT_BOOT,
+            DETECT_VM_INCORRECT_CONTEXT_USE,
+            DETECT_VM_UNSAFE_INTENT_LAUNCH,
+            PENALTY_GATHER,
+            PENALTY_LOG,
+            PENALTY_DIALOG,
+            PENALTY_DEATH,
+            PENALTY_FLASH,
+            PENALTY_DROPBOX,
+            PENALTY_DEATH_ON_NETWORK,
+            PENALTY_DEATH_ON_CLEARTEXT_NETWORK,
+            PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VmPolicyMask {}
+
+    // VM policy: bits 0-15
+
+    /** @hide */
+    private static final int DETECT_VM_CURSOR_LEAKS = 1 << 0;
+    /** @hide */
+    private static final int DETECT_VM_CLOSABLE_LEAKS = 1 << 1;
+    /** @hide */
+    private static final int DETECT_VM_ACTIVITY_LEAKS = 1 << 2;
+    /** @hide */
+    private static final int DETECT_VM_INSTANCE_LEAKS = 1 << 3;
+    /** @hide */
+    private static final int DETECT_VM_REGISTRATION_LEAKS = 1 << 4;
+    /** @hide */
+    private static final int DETECT_VM_FILE_URI_EXPOSURE = 1 << 5;
+    /** @hide */
+    private static final int DETECT_VM_CLEARTEXT_NETWORK = 1 << 6;
+    /** @hide */
+    private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 1 << 7;
+    /** @hide */
+    private static final int DETECT_VM_UNTAGGED_SOCKET = 1 << 8;
+    /** @hide */
+    private static final int DETECT_VM_NON_SDK_API_USAGE = 1 << 9;
+    /** @hide */
+    private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10;
+    /** @hide */
+    private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11;
+    /** @hide */
+    private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12;
+    /** @hide */
+    private static final int DETECT_VM_UNSAFE_INTENT_LAUNCH = 1 << 13;
+
+    /** @hide */
+    private static final int DETECT_VM_ALL = 0x0000ffff;
+
+    // Penalty policy: bits 16-31
+
+    /**
+     * Non-public penalty mode which overrides all the other penalty bits and signals that we're in
+     * a Binder call and we should ignore the other penalty bits and instead serialize back all our
+     * offending stack traces to the caller to ultimately handle in the originating process.
+     *
+     * <p>This must be kept in sync with the constant in libs/binder/Parcel.cpp
+     *
+     * @hide
+     */
+    public static final int PENALTY_GATHER = 1 << 31;
+
+    /** {@hide} */
+    public static final int PENALTY_LOG = 1 << 30;
+    /** {@hide} */
+    public static final int PENALTY_DIALOG = 1 << 29;
+    /** {@hide} */
+    public static final int PENALTY_DEATH = 1 << 28;
+    /** {@hide} */
+    public static final int PENALTY_FLASH = 1 << 27;
+    /** {@hide} */
+    public static final int PENALTY_DROPBOX = 1 << 26;
+    /** {@hide} */
+    public static final int PENALTY_DEATH_ON_NETWORK = 1 << 25;
+    /** {@hide} */
+    public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 1 << 24;
+    /** {@hide} */
+    public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 1 << 23;
+
+    /** @hide */
+    public static final int PENALTY_ALL = 0xffff0000;
+
+    /** {@hide} */
+    public static final int NETWORK_POLICY_ACCEPT = 0;
+    /** {@hide} */
+    public static final int NETWORK_POLICY_LOG = 1;
+    /** {@hide} */
+    public static final int NETWORK_POLICY_REJECT = 2;
+  
+    /**
+     * Detect explicit calls to {@link Runtime#gc()}.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    static final long DETECT_EXPLICIT_GC = 3400644L;
+
+    // TODO: wrap in some ImmutableHashMap thing.
+    // Note: must be before static initialization of sVmPolicy.
+    private static final HashMap<Class, Integer> EMPTY_CLASS_LIMIT_MAP =
+            new HashMap<Class, Integer>();
+
+    /** The current VmPolicy in effect. */
+    private static volatile VmPolicy sVmPolicy = VmPolicy.LAX;
+
+    /** {@hide} */
+    @TestApi
+    public interface ViolationLogger {
+
+        /** Called when penaltyLog is enabled and a violation needs logging. */
+        void log(ViolationInfo info);
+    }
+
+    private static final ViolationLogger LOGCAT_LOGGER =
+            info -> {
+                String msg;
+                if (info.durationMillis != -1) {
+                    msg = "StrictMode policy violation; ~duration=" + info.durationMillis + " ms:";
+                } else {
+                    msg = "StrictMode policy violation:";
+                }
+                Log.d(TAG, msg + " " + info.getStackTrace());
+            };
+
+    private static volatile ViolationLogger sLogger = LOGCAT_LOGGER;
+
+    private static final ThreadLocal<OnThreadViolationListener> sThreadViolationListener =
+            new ThreadLocal<>();
+    private static final ThreadLocal<Executor> sThreadViolationExecutor = new ThreadLocal<>();
+
+    /**
+     * When #{@link ThreadPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+     * provided executor when a Thread violation occurs.
+     */
+    public interface OnThreadViolationListener {
+        /** Called on a thread policy violation. */
+        void onThreadViolation(Violation v);
+    }
+
+    /**
+     * When #{@link VmPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+     * provided executor when a VM violation occurs.
+     */
+    public interface OnVmViolationListener {
+        /** Called on a VM policy violation. */
+        void onVmViolation(Violation v);
+    }
+
+    /** {@hide} */
+    @TestApi
+    public static void setViolationLogger(ViolationLogger listener) {
+        if (listener == null) {
+            listener = LOGCAT_LOGGER;
+        }
+        sLogger = listener;
+    }
+
+    /**
+     * The number of threads trying to do an async dropbox write. Just to limit ourselves out of
+     * paranoia.
+     */
+    private static final AtomicInteger sDropboxCallsInFlight = new AtomicInteger(0);
+
+    /**
+     * Callback supplied to dalvik / libcore to get informed of usages of java API that are not
+     * a part of the public SDK.
+     */
+    private static final Consumer<String> sNonSdkApiUsageConsumer =
+            message -> onVmPolicyViolation(new NonSdkApiUsedViolation(message));
+
+    private StrictMode() {}
+
+    /**
+     * {@link StrictMode} policy applied to a certain thread.
+     *
+     * <p>The policy is enabled by {@link #setThreadPolicy}. The current policy can be retrieved
+     * with {@link #getThreadPolicy}.
+     *
+     * <p>Note that multiple penalties may be provided and they're run in order from least to most
+     * severe (logging before process death, for example). There's currently no mechanism to choose
+     * different penalties for different detected actions.
+     */
+    public static final class ThreadPolicy {
+        /** The lax policy which doesn't catch anything. */
+        public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
+
+        @UnsupportedAppUsage
+        final @ThreadPolicyMask int mask;
+        final OnThreadViolationListener mListener;
+        final Executor mCallbackExecutor;
+
+        private ThreadPolicy(@ThreadPolicyMask int mask, OnThreadViolationListener listener,
+                Executor executor) {
+            this.mask = mask;
+            mListener = listener;
+            mCallbackExecutor = executor;
+        }
+
+        @Override
+        public String toString() {
+            return "[StrictMode.ThreadPolicy; mask=" + mask + "]";
+        }
+
+        /**
+         * Creates {@link ThreadPolicy} instances. Methods whose names start with {@code detect}
+         * specify what problems we should look for. Methods whose names start with {@code penalty}
+         * specify what we should do when we detect a problem.
+         *
+         * <p>You can call as many {@code detect} and {@code penalty} methods as you like. Currently
+         * order is insignificant: all penalties apply to all detected problems.
+         *
+         * <p>For example, detect everything and log anything that's found:
+         *
+         * <pre>
+         * StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
+         *     .detectAll()
+         *     .penaltyLog()
+         *     .build();
+         * StrictMode.setThreadPolicy(policy);
+         * </pre>
+         */
+        public static final class Builder {
+            private @ThreadPolicyMask int mMask = 0;
+            private OnThreadViolationListener mListener;
+            private Executor mExecutor;
+
+            /**
+             * Create a Builder that detects nothing and has no violations. (but note that {@link
+             * #build} will default to enabling {@link #penaltyLog} if no other penalties are
+             * specified)
+             */
+            public Builder() {
+                mMask = 0;
+            }
+
+            /** Initialize a Builder from an existing ThreadPolicy. */
+            public Builder(ThreadPolicy policy) {
+                mMask = policy.mask;
+                mListener = policy.mListener;
+                mExecutor = policy.mCallbackExecutor;
+            }
+
+            /**
+             * Detect everything that's potentially suspect.
+             *
+             * <p>As of the Gingerbread release this includes network and disk operations but will
+             * likely expand in future releases.
+             */
+            @SuppressWarnings("AndroidFrameworkCompatChange")
+            public @NonNull Builder detectAll() {
+                detectDiskReads();
+                detectDiskWrites();
+                detectNetwork();
+
+                final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
+                if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
+                    detectCustomSlowCalls();
+                }
+                if (targetSdk >= Build.VERSION_CODES.M) {
+                    detectResourceMismatches();
+                }
+                if (targetSdk >= Build.VERSION_CODES.O) {
+                    detectUnbufferedIo();
+                }
+                if (CompatChanges.isChangeEnabled(DETECT_EXPLICIT_GC)) {
+                    detectExplicitGc();
+                }
+                return this;
+            }
+
+            /** Disable the detection of everything. */
+            public @NonNull Builder permitAll() {
+                return disable(DETECT_THREAD_ALL);
+            }
+
+            /** Enable detection of network operations. */
+            public @NonNull Builder detectNetwork() {
+                return enable(DETECT_THREAD_NETWORK);
+            }
+
+            /** Disable detection of network operations. */
+            public @NonNull Builder permitNetwork() {
+                return disable(DETECT_THREAD_NETWORK);
+            }
+
+            /** Enable detection of disk reads. */
+            public @NonNull Builder detectDiskReads() {
+                return enable(DETECT_THREAD_DISK_READ);
+            }
+
+            /** Disable detection of disk reads. */
+            public @NonNull Builder permitDiskReads() {
+                return disable(DETECT_THREAD_DISK_READ);
+            }
+
+            /** Enable detection of slow calls. */
+            public @NonNull Builder detectCustomSlowCalls() {
+                return enable(DETECT_THREAD_CUSTOM);
+            }
+
+            /** Disable detection of slow calls. */
+            public @NonNull Builder permitCustomSlowCalls() {
+                return disable(DETECT_THREAD_CUSTOM);
+            }
+
+            /** Disable detection of mismatches between defined resource types and getter calls. */
+            public @NonNull Builder permitResourceMismatches() {
+                return disable(DETECT_THREAD_RESOURCE_MISMATCH);
+            }
+
+            /** Detect unbuffered input/output operations. */
+            public @NonNull Builder detectUnbufferedIo() {
+                return enable(DETECT_THREAD_UNBUFFERED_IO);
+            }
+
+            /** Disable detection of unbuffered input/output operations. */
+            public @NonNull Builder permitUnbufferedIo() {
+                return disable(DETECT_THREAD_UNBUFFERED_IO);
+            }
+
+            /**
+             * Enables detection of mismatches between defined resource types and getter calls.
+             *
+             * <p>This helps detect accidental type mismatches and potentially expensive type
+             * conversions when obtaining typed resources.
+             *
+             * <p>For example, a strict mode violation would be thrown when calling {@link
+             * android.content.res.TypedArray#getInt(int, int)} on an index that contains a
+             * String-type resource. If the string value can be parsed as an integer, this method
+             * call will return a value without crashing; however, the developer should format the
+             * resource as an integer to avoid unnecessary type conversion.
+             */
+            public @NonNull Builder detectResourceMismatches() {
+                return enable(DETECT_THREAD_RESOURCE_MISMATCH);
+            }
+
+            /** Enable detection of disk writes. */
+            public @NonNull Builder detectDiskWrites() {
+                return enable(DETECT_THREAD_DISK_WRITE);
+            }
+
+            /** Disable detection of disk writes. */
+            public @NonNull Builder permitDiskWrites() {
+                return disable(DETECT_THREAD_DISK_WRITE);
+            }
+
+            /**
+             * Detect calls to {@link Runtime#gc()}.
+             */
+            public @NonNull Builder detectExplicitGc() {
+                return enable(DETECT_THREAD_EXPLICIT_GC);
+            }
+
+            /**
+             * Disable detection of calls to {@link Runtime#gc()}.
+             */
+            public @NonNull Builder permitExplicitGc() {
+                return disable(DETECT_THREAD_EXPLICIT_GC);
+            }
+
+            /**
+             * Show an annoying dialog to the developer on detected violations, rate-limited to be
+             * only a little annoying.
+             */
+            public @NonNull Builder penaltyDialog() {
+                return enable(PENALTY_DIALOG);
+            }
+
+            /**
+             * Crash the whole process on violation. This penalty runs at the end of all enabled
+             * penalties so you'll still get see logging or other violations before the process
+             * dies.
+             *
+             * <p>Unlike {@link #penaltyDeathOnNetwork}, this applies to disk reads, disk writes,
+             * and network usage if their corresponding detect flags are set.
+             */
+            public @NonNull Builder penaltyDeath() {
+                return enable(PENALTY_DEATH);
+            }
+
+            /**
+             * Crash the whole process on any network usage. Unlike {@link #penaltyDeath}, this
+             * penalty runs <em>before</em> anything else. You must still have called {@link
+             * #detectNetwork} to enable this.
+             *
+             * <p>In the Honeycomb or later SDKs, this is on by default.
+             */
+            public @NonNull Builder penaltyDeathOnNetwork() {
+                return enable(PENALTY_DEATH_ON_NETWORK);
+            }
+
+            /** Flash the screen during a violation. */
+            public @NonNull Builder penaltyFlashScreen() {
+                return enable(PENALTY_FLASH);
+            }
+
+            /** Log detected violations to the system log. */
+            public @NonNull Builder penaltyLog() {
+                return enable(PENALTY_LOG);
+            }
+
+            /**
+             * Enable detected violations log a stacktrace and timing data to the {@link
+             * android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform
+             * integrators doing beta user field data collection.
+             */
+            public @NonNull Builder penaltyDropBox() {
+                return enable(PENALTY_DROPBOX);
+            }
+
+            /**
+             * Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
+             * executor every violation.
+             */
+            public @NonNull Builder penaltyListener(
+                    @NonNull Executor executor, @NonNull OnThreadViolationListener listener) {
+                if (executor == null) {
+                    throw new NullPointerException("executor must not be null");
+                }
+                mListener = listener;
+                mExecutor = executor;
+                return this;
+            }
+
+            /** @removed */
+            public @NonNull Builder penaltyListener(
+                    @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+                return penaltyListener(executor, listener);
+            }
+
+            private Builder enable(@ThreadPolicyMask int mask) {
+                mMask |= mask;
+                return this;
+            }
+
+            private Builder disable(@ThreadPolicyMask int mask) {
+                mMask &= ~mask;
+                return this;
+            }
+
+            /**
+             * Construct the ThreadPolicy instance.
+             *
+             * <p>Note: if no penalties are enabled before calling <code>build</code>, {@link
+             * #penaltyLog} is implicitly set.
+             */
+            public ThreadPolicy build() {
+                // If there are detection bits set but no violation bits
+                // set, enable simple logging.
+                if (mListener == null
+                        && mMask != 0
+                        && (mMask
+                                        & (PENALTY_DEATH
+                                                | PENALTY_LOG
+                                                | PENALTY_DROPBOX
+                                                | PENALTY_DIALOG))
+                                == 0) {
+                    penaltyLog();
+                }
+                return new ThreadPolicy(mMask, mListener, mExecutor);
+            }
+        }
+    }
+
+    /**
+     * {@link StrictMode} policy applied to all threads in the virtual machine's process.
+     *
+     * <p>The policy is enabled by {@link #setVmPolicy}.
+     */
+    public static final class VmPolicy {
+        /** The lax policy which doesn't catch anything. */
+        public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
+
+        @UnsupportedAppUsage
+        final @VmPolicyMask int mask;
+        final OnVmViolationListener mListener;
+        final Executor mCallbackExecutor;
+
+        // Map from class to max number of allowed instances in memory.
+        final HashMap<Class, Integer> classInstanceLimit;
+
+        private VmPolicy(
+                @VmPolicyMask int mask,
+                HashMap<Class, Integer> classInstanceLimit,
+                OnVmViolationListener listener,
+                Executor executor) {
+            if (classInstanceLimit == null) {
+                throw new NullPointerException("classInstanceLimit == null");
+            }
+            this.mask = mask;
+            this.classInstanceLimit = classInstanceLimit;
+            mListener = listener;
+            mCallbackExecutor = executor;
+        }
+
+        @Override
+        public String toString() {
+            return "[StrictMode.VmPolicy; mask=" + mask + "]";
+        }
+
+        /**
+         * Creates {@link VmPolicy} instances. Methods whose names start with {@code detect} specify
+         * what problems we should look for. Methods whose names start with {@code penalty} specify
+         * what we should do when we detect a problem.
+         *
+         * <p>You can call as many {@code detect} and {@code penalty} methods as you like. Currently
+         * order is insignificant: all penalties apply to all detected problems.
+         *
+         * <p>For example, detect everything and log anything that's found:
+         *
+         * <pre>
+         * StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
+         *     .detectAll()
+         *     .penaltyLog()
+         *     .build();
+         * StrictMode.setVmPolicy(policy);
+         * </pre>
+         */
+        public static final class Builder {
+            @UnsupportedAppUsage
+            private @VmPolicyMask int mMask;
+            private OnVmViolationListener mListener;
+            private Executor mExecutor;
+
+            private HashMap<Class, Integer> mClassInstanceLimit; // null until needed
+            private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write
+
+            public Builder() {
+                mMask = 0;
+            }
+
+            /** Build upon an existing VmPolicy. */
+            public Builder(VmPolicy base) {
+                mMask = base.mask;
+                mClassInstanceLimitNeedCow = true;
+                mClassInstanceLimit = base.classInstanceLimit;
+                mListener = base.mListener;
+                mExecutor = base.mCallbackExecutor;
+            }
+
+            /**
+             * Set an upper bound on how many instances of a class can be in memory at once. Helps
+             * to prevent object leaks.
+             */
+            public @NonNull Builder setClassInstanceLimit(Class klass, int instanceLimit) {
+                if (klass == null) {
+                    throw new NullPointerException("klass == null");
+                }
+                if (mClassInstanceLimitNeedCow) {
+                    if (mClassInstanceLimit.containsKey(klass)
+                            && mClassInstanceLimit.get(klass) == instanceLimit) {
+                        // no-op; don't break COW
+                        return this;
+                    }
+                    mClassInstanceLimitNeedCow = false;
+                    mClassInstanceLimit = (HashMap<Class, Integer>) mClassInstanceLimit.clone();
+                } else if (mClassInstanceLimit == null) {
+                    mClassInstanceLimit = new HashMap<Class, Integer>();
+                }
+                mMask |= DETECT_VM_INSTANCE_LEAKS;
+                mClassInstanceLimit.put(klass, instanceLimit);
+                return this;
+            }
+
+            /** Detect leaks of {@link android.app.Activity} subclasses. */
+            public @NonNull Builder detectActivityLeaks() {
+                return enable(DETECT_VM_ACTIVITY_LEAKS);
+            }
+
+            /** @hide */
+            public @NonNull Builder permitActivityLeaks() {
+                synchronized (StrictMode.class) {
+                    sExpectedActivityInstanceCount.clear();
+                }
+                return disable(DETECT_VM_ACTIVITY_LEAKS);
+            }
+
+            /**
+             * Detect reflective usage of APIs that are not part of the public Android SDK.
+             *
+             * <p>Note that any non-SDK APIs that this processes accesses before this detection is
+             * enabled may not be detected. To ensure that all such API accesses are detected,
+             * you should apply this policy as early as possible after process creation.
+             */
+            public @NonNull Builder detectNonSdkApiUsage() {
+                return enable(DETECT_VM_NON_SDK_API_USAGE);
+            }
+
+            /**
+             * Permit reflective usage of APIs that are not part of the public Android SDK. Note
+             * that this <b>only</b> affects {@code StrictMode}, the underlying runtime may
+             * continue to restrict or warn on access to methods that are not part of the
+             * public SDK.
+             */
+            public @NonNull Builder permitNonSdkApiUsage() {
+                return disable(DETECT_VM_NON_SDK_API_USAGE);
+            }
+
+            /**
+             * Detect everything that's potentially suspect.
+             *
+             * <p>In the Honeycomb release this includes leaks of SQLite cursors, Activities, and
+             * other closable objects but will likely expand in future releases.
+             */
+            @SuppressWarnings("AndroidFrameworkCompatChange")
+            public @NonNull Builder detectAll() {
+                detectLeakedSqlLiteObjects();
+
+                final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
+                if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
+                    detectActivityLeaks();
+                    detectLeakedClosableObjects();
+                }
+                if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN) {
+                    detectLeakedRegistrationObjects();
+                }
+                if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+                    detectFileUriExposure();
+                }
+                if (targetSdk >= Build.VERSION_CODES.M) {
+                    // TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have
+                    // facility for apps to mark sockets that should be ignored
+                    if (SystemProperties.getBoolean(CLEARTEXT_PROPERTY, false)) {
+                        detectCleartextNetwork();
+                    }
+                }
+                if (targetSdk >= Build.VERSION_CODES.O) {
+                    detectContentUriWithoutPermission();
+                    detectUntaggedSockets();
+                }
+                if (targetSdk >= Build.VERSION_CODES.Q) {
+                    detectCredentialProtectedWhileLocked();
+                }
+                if (targetSdk >= Build.VERSION_CODES.R) {
+                    detectIncorrectContextUse();
+                }
+                if (targetSdk >= Build.VERSION_CODES.S) {
+                    detectUnsafeIntentLaunch();
+                }
+
+                // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
+                // TODO: enable detectImplicitDirectBoot() once system is less noisy
+
+                return this;
+            }
+
+            /**
+             * Detect when an {@link android.database.sqlite.SQLiteCursor} or other SQLite object is
+             * finalized without having been closed.
+             *
+             * <p>You always want to explicitly close your SQLite cursors to avoid unnecessary
+             * database contention and temporary memory leaks.
+             */
+            public @NonNull Builder detectLeakedSqlLiteObjects() {
+                return enable(DETECT_VM_CURSOR_LEAKS);
+            }
+
+            /**
+             * 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
+             * leaks.
+             */
+            public @NonNull Builder detectLeakedClosableObjects() {
+                return enable(DETECT_VM_CLOSABLE_LEAKS);
+            }
+
+            /**
+             * Detect when a {@link BroadcastReceiver} or {@link ServiceConnection} is leaked during
+             * {@link Context} teardown.
+             */
+            public @NonNull Builder detectLeakedRegistrationObjects() {
+                return enable(DETECT_VM_REGISTRATION_LEAKS);
+            }
+
+            /**
+             * Detect when the calling application exposes a {@code file://} {@link android.net.Uri}
+             * to another app.
+             *
+             * <p>This exposure is discouraged since the receiving app may not have access to the
+             * shared path. For example, the receiving app may not have requested the {@link
+             * android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission, or the
+             * platform may be sharing the {@link android.net.Uri} across user profile boundaries.
+             *
+             * <p>Instead, apps should use {@code content://} Uris so the platform can extend
+             * temporary permission for the receiving app to access the resource.
+             *
+             * @see androidx.core.content.FileProvider
+             * @see Intent#FLAG_GRANT_READ_URI_PERMISSION
+             */
+            public @NonNull Builder detectFileUriExposure() {
+                return enable(DETECT_VM_FILE_URI_EXPOSURE);
+            }
+
+            /**
+             * Detect any network traffic from the calling app which is not wrapped in SSL/TLS. This
+             * can help you detect places that your app is inadvertently sending cleartext data
+             * across the network.
+             *
+             * <p>Using {@link #penaltyDeath()} or {@link #penaltyDeathOnCleartextNetwork()} will
+             * block further traffic on that socket to prevent accidental data leakage, in addition
+             * to crashing your process.
+             *
+             * <p>Using {@link #penaltyDropBox()} will log the raw contents of the packet that
+             * triggered the violation.
+             *
+             * <p>This inspects both IPv4/IPv6 and TCP/UDP network traffic, but it may be subject to
+             * false positives, such as when STARTTLS protocols or HTTP proxies are used.
+             */
+            public @NonNull Builder detectCleartextNetwork() {
+                return enable(DETECT_VM_CLEARTEXT_NETWORK);
+            }
+
+            /**
+             * Detect when the calling application sends a {@code content://} {@link
+             * android.net.Uri} to another app without setting {@link
+             * Intent#FLAG_GRANT_READ_URI_PERMISSION} or {@link
+             * Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
+             *
+             * <p>Forgetting to include one or more of these flags when sending an intent is
+             * typically an app bug.
+             *
+             * @see Intent#FLAG_GRANT_READ_URI_PERMISSION
+             * @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION
+             */
+            public @NonNull Builder detectContentUriWithoutPermission() {
+                return enable(DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION);
+            }
+
+            /**
+             * Detect any sockets in the calling app which have not been tagged using {@link
+             * TrafficStats}. Tagging sockets can help you investigate network usage inside your
+             * app, such as a narrowing down heavy usage to a specific library or component.
+             *
+             * <p>This currently does not detect sockets created in native code.
+             *
+             * @see TrafficStats#setThreadStatsTag(int)
+             * @see TrafficStats#tagSocket(java.net.Socket)
+             * @see TrafficStats#tagDatagramSocket(java.net.DatagramSocket)
+             */
+            public @NonNull Builder detectUntaggedSockets() {
+                return enable(DETECT_VM_UNTAGGED_SOCKET);
+            }
+
+            /** @hide */
+            public @NonNull Builder permitUntaggedSockets() {
+                return disable(DETECT_VM_UNTAGGED_SOCKET);
+            }
+
+            /**
+             * Detect any implicit reliance on Direct Boot automatic filtering
+             * of {@link PackageManager} values. Violations are only triggered
+             * when implicit calls are made while the user is locked.
+             * <p>
+             * Apps becoming Direct Boot aware need to carefully inspect each
+             * query site and explicitly decide which combination of flags they
+             * want to use:
+             * <ul>
+             * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AWARE}
+             * <li>{@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE}
+             * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AUTO}
+             * </ul>
+             */
+            public @NonNull Builder detectImplicitDirectBoot() {
+                return enable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
+            }
+
+            /** @hide */
+            public @NonNull Builder permitImplicitDirectBoot() {
+                return disable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
+            }
+
+            /**
+             * Detect access to filesystem paths stored in credential protected
+             * storage areas while the user is locked.
+             * <p>
+             * When a user is locked, credential protected storage is
+             * unavailable, and files stored in these locations appear to not
+             * exist, which can result in subtle app bugs if they assume default
+             * behaviors or empty states. Instead, apps should store data needed
+             * while a user is locked under device protected storage areas.
+             *
+             * @see Context#createDeviceProtectedStorageContext()
+             */
+            public @NonNull Builder detectCredentialProtectedWhileLocked() {
+                return enable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
+            }
+
+            /** @hide */
+            public @NonNull Builder permitCredentialProtectedWhileLocked() {
+                return disable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
+            }
+
+            /**
+             * Detect attempts to invoke a method on a {@link Context} that is not suited for such
+             * operation.
+             * <p>An example of this is trying to obtain an instance of UI service (e.g.
+             * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not
+             * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and
+             * therefore can report incorrect metrics or resources.
+             * @see Context#getDisplay()
+             * @see Context#getSystemService(String)
+             */
+            public @NonNull Builder detectIncorrectContextUse() {
+                return enable(DETECT_VM_INCORRECT_CONTEXT_USE);
+            }
+
+            /**
+             * Disable detection of incorrect context use.
+             *
+             * @see #detectIncorrectContextUse()
+             *
+             * @hide
+             */
+            @TestApi
+            public @NonNull Builder permitIncorrectContextUse() {
+                return disable(DETECT_VM_INCORRECT_CONTEXT_USE);
+            }
+
+            /**
+             * Detect when your app sends an unsafe {@link Intent}.
+             * <p>
+             * Violations may indicate security vulnerabilities in the design of
+             * your app, where a malicious app could trick you into granting
+             * {@link Uri} permissions or launching unexported components. Here
+             * are some typical design patterns that can be used to safely
+             * resolve these violations:
+             * <ul>
+             * <li> If you are sending an implicit intent to an unexported component, you should
+             * make it an explicit intent by using {@link Intent#setPackage},
+             * {@link Intent#setClassName} or {@link Intent#setComponent}.
+             * </li>
+             * <li> If you are unparceling and sending an intent from the intent delivered, The
+             * ideal approach is to migrate to using a {@link android.app.PendingIntent}, which
+             * ensures that your launch is performed using the identity of the original creator,
+             * completely avoiding the security issues described above.
+             * <li>If using a {@link android.app.PendingIntent} isn't feasible, an
+             * alternative approach is to create a brand new {@link Intent} and
+             * carefully copy only specific values from the original
+             * {@link Intent} after careful validation.
+             * </ul>
+             * <p>
+             * Note that this <em>may</em> detect false-positives if your app
+             * sends itself an {@link Intent} which is first routed through the
+             * OS, such as using {@link Intent#createChooser}. In these cases,
+             * careful inspection is required to determine if the return point
+             * into your app is appropriately protected with a signature
+             * permission or marked as unexported. If the return point is not
+             * protected, your app is likely vulnerable to malicious apps.
+             *
+             * @see Context#startActivity(Intent)
+             * @see Context#startService(Intent)
+             * @see Context#bindService(Intent, ServiceConnection, int)
+             * @see Context#sendBroadcast(Intent)
+             * @see android.app.Activity#setResult(int, Intent)
+             */
+            public @NonNull Builder detectUnsafeIntentLaunch() {
+                return enable(DETECT_VM_UNSAFE_INTENT_LAUNCH);
+            }
+
+            /**
+             * Permit your app to launch any {@link Intent} which originated
+             * from outside your app.
+             * <p>
+             * Disabling this check is <em>strongly discouraged</em>, as
+             * violations may indicate security vulnerabilities in the design of
+             * your app, where a malicious app could trick you into granting
+             * {@link Uri} permissions or launching unexported components.
+             *
+             * @see #detectUnsafeIntentLaunch()
+             */
+            public @NonNull Builder permitUnsafeIntentLaunch() {
+                return disable(DETECT_VM_UNSAFE_INTENT_LAUNCH);
+            }
+
+            /**
+             * Crashes the whole process on violation. This penalty runs at the end of all enabled
+             * penalties so you'll still get your logging or other violations before the process
+             * dies.
+             */
+            public @NonNull Builder penaltyDeath() {
+                return enable(PENALTY_DEATH);
+            }
+
+            /**
+             * Crashes the whole process when cleartext network traffic is detected.
+             *
+             * @see #detectCleartextNetwork()
+             */
+            public @NonNull Builder penaltyDeathOnCleartextNetwork() {
+                return enable(PENALTY_DEATH_ON_CLEARTEXT_NETWORK);
+            }
+
+            /**
+             * Crashes the whole process when a {@code file://} {@link android.net.Uri} is exposed
+             * beyond this app.
+             *
+             * @see #detectFileUriExposure()
+             */
+            public @NonNull Builder penaltyDeathOnFileUriExposure() {
+                return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
+            }
+
+            /** Log detected violations to the system log. */
+            public @NonNull Builder penaltyLog() {
+                return enable(PENALTY_LOG);
+            }
+
+            /**
+             * Enable detected violations log a stacktrace and timing data to the {@link
+             * android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform
+             * integrators doing beta user field data collection.
+             */
+            public @NonNull Builder penaltyDropBox() {
+                return enable(PENALTY_DROPBOX);
+            }
+
+            /**
+             * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
+             */
+            public @NonNull Builder penaltyListener(
+                    @NonNull Executor executor, @NonNull OnVmViolationListener listener) {
+                if (executor == null) {
+                    throw new NullPointerException("executor must not be null");
+                }
+                mListener = listener;
+                mExecutor = executor;
+                return this;
+            }
+
+            /** @removed */
+            public @NonNull Builder penaltyListener(
+                    @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+                return penaltyListener(executor, listener);
+            }
+
+            private Builder enable(@VmPolicyMask int mask) {
+                mMask |= mask;
+                return this;
+            }
+
+            Builder disable(@VmPolicyMask int mask) {
+                mMask &= ~mask;
+                return this;
+            }
+
+            /**
+             * Construct the VmPolicy instance.
+             *
+             * <p>Note: if no penalties are enabled before calling <code>build</code>, {@link
+             * #penaltyLog} is implicitly set.
+             */
+            public VmPolicy build() {
+                // If there are detection bits set but no violation bits
+                // set, enable simple logging.
+                if (mListener == null
+                        && mMask != 0
+                        && (mMask
+                                        & (PENALTY_DEATH
+                                                | PENALTY_LOG
+                                                | PENALTY_DROPBOX
+                                                | PENALTY_DIALOG))
+                                == 0) {
+                    penaltyLog();
+                }
+                return new VmPolicy(
+                        mMask,
+                        mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP,
+                        mListener,
+                        mExecutor);
+            }
+        }
+    }
+
+    /**
+     * Log of strict mode violation stack traces that have occurred during a Binder call, to be
+     * serialized back later to the caller via Parcel.writeNoException() (amusingly) where the
+     * caller can choose how to react.
+     */
+    private static final ThreadLocal<ArrayList<ViolationInfo>> gatheredViolations =
+            new ThreadLocal<ArrayList<ViolationInfo>>() {
+                @Override
+                protected ArrayList<ViolationInfo> initialValue() {
+                    // Starts null to avoid unnecessary allocations when
+                    // checking whether there are any violations or not in
+                    // hasGatheredViolations() below.
+                    return null;
+                }
+            };
+
+    /**
+     * Sets the policy for what actions on the current thread should be detected, as well as the
+     * penalty if such actions occur.
+     *
+     * <p>Internally this sets a thread-local variable which is propagated across cross-process IPC
+     * calls, meaning you can catch violations when a system service or another process accesses the
+     * disk or network on your behalf.
+     *
+     * @param policy the policy to put into place
+     */
+    public static void setThreadPolicy(final ThreadPolicy policy) {
+        setThreadPolicyMask(policy.mask);
+        sThreadViolationListener.set(policy.mListener);
+        sThreadViolationExecutor.set(policy.mCallbackExecutor);
+    }
+
+    /** @hide */
+    public static void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
+        // In addition to the Java-level thread-local in Dalvik's
+        // BlockGuard, we also need to keep a native thread-local in
+        // Binder in order to propagate the value across Binder calls,
+        // even across native-only processes.  The two are kept in
+        // sync via the callback to onStrictModePolicyChange, below.
+        setBlockGuardPolicy(threadPolicyMask);
+
+        // And set the Android native version...
+        Binder.setThreadStrictModePolicy(threadPolicyMask);
+    }
+
+    // Sets the policy in Dalvik/libcore (BlockGuard)
+    private static void setBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
+        if (threadPolicyMask == 0) {
+            BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
+            return;
+        }
+        final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        final AndroidBlockGuardPolicy androidPolicy;
+        if (policy instanceof AndroidBlockGuardPolicy) {
+            androidPolicy = (AndroidBlockGuardPolicy) policy;
+        } else {
+            androidPolicy = THREAD_ANDROID_POLICY.get();
+            BlockGuard.setThreadPolicy(androidPolicy);
+        }
+        androidPolicy.setThreadPolicyMask(threadPolicyMask);
+    }
+
+    private static void setBlockGuardVmPolicy(@VmPolicyMask int vmPolicyMask) {
+        // We only need to install BlockGuard for a small subset of VM policies
+        vmPolicyMask &= DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED;
+        if (vmPolicyMask != 0) {
+            BlockGuard.setVmPolicy(VM_ANDROID_POLICY);
+        } else {
+            BlockGuard.setVmPolicy(BlockGuard.LAX_VM_POLICY);
+        }
+    }
+
+    // Sets up CloseGuard in Dalvik/libcore
+    private static void setCloseGuardEnabled(boolean enabled) {
+        if (!(CloseGuard.getReporter() instanceof AndroidCloseGuardReporter)) {
+            CloseGuard.setReporter(new AndroidCloseGuardReporter());
+        }
+        CloseGuard.setEnabled(enabled);
+    }
+
+    /**
+     * Returns the bitmask of the current thread's policy.
+     *
+     * @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static @ThreadPolicyMask int getThreadPolicyMask() {
+        final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (policy instanceof AndroidBlockGuardPolicy) {
+            return ((AndroidBlockGuardPolicy) policy).getThreadPolicyMask();
+        } else {
+            return 0;
+        }
+    }
+
+    /** Returns the current thread's policy. */
+    public static ThreadPolicy getThreadPolicy() {
+        // TODO: this was a last minute Gingerbread API change (to
+        // introduce VmPolicy cleanly) but this isn't particularly
+        // optimal for users who might call this method often.  This
+        // should be in a thread-local and not allocate on each call.
+        return new ThreadPolicy(
+                getThreadPolicyMask(),
+                sThreadViolationListener.get(),
+                sThreadViolationExecutor.get());
+    }
+
+    /**
+     * A convenience wrapper that takes the current {@link ThreadPolicy} from {@link
+     * #getThreadPolicy}, modifies it to permit both disk reads &amp; writes, and sets the new
+     * policy with {@link #setThreadPolicy}, returning the old policy so you can restore it at the
+     * end of a block.
+     *
+     * @return the old policy, to be passed to {@link #setThreadPolicy} to restore the policy at the
+     *     end of a block
+     */
+    public static ThreadPolicy allowThreadDiskWrites() {
+        return new ThreadPolicy(
+                allowThreadDiskWritesMask(),
+                sThreadViolationListener.get(),
+                sThreadViolationExecutor.get());
+    }
+
+    /** @hide */
+    public static @ThreadPolicyMask int allowThreadDiskWritesMask() {
+        int oldPolicyMask = getThreadPolicyMask();
+        int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_WRITE | DETECT_THREAD_DISK_READ);
+        if (newPolicyMask != oldPolicyMask) {
+            setThreadPolicyMask(newPolicyMask);
+        }
+        return oldPolicyMask;
+    }
+
+    /**
+     * A convenience wrapper that takes the current {@link ThreadPolicy} from {@link
+     * #getThreadPolicy}, modifies it to permit disk reads, and sets the new policy with {@link
+     * #setThreadPolicy}, returning the old policy so you can restore it at the end of a block.
+     *
+     * @return the old policy, to be passed to setThreadPolicy to restore the policy.
+     */
+    public static ThreadPolicy allowThreadDiskReads() {
+        return new ThreadPolicy(
+                allowThreadDiskReadsMask(),
+                sThreadViolationListener.get(),
+                sThreadViolationExecutor.get());
+    }
+
+    /** @hide */
+    public static @ThreadPolicyMask int allowThreadDiskReadsMask() {
+        int oldPolicyMask = getThreadPolicyMask();
+        int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_READ);
+        if (newPolicyMask != oldPolicyMask) {
+            setThreadPolicyMask(newPolicyMask);
+        }
+        return oldPolicyMask;
+    }
+
+    /** @hide */
+    public static ThreadPolicy allowThreadViolations() {
+        ThreadPolicy oldPolicy = getThreadPolicy();
+        setThreadPolicyMask(0);
+        return oldPolicy;
+    }
+
+    /** @hide */
+    public static VmPolicy allowVmViolations() {
+        VmPolicy oldPolicy = getVmPolicy();
+        sVmPolicy = VmPolicy.LAX;
+        return oldPolicy;
+    }
+
+    /**
+     * Determine if the given app is "bundled" as part of the system image. These bundled apps are
+     * developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to
+     * chase any {@link StrictMode} regressions by enabling detection when running on {@link
+     * Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
+     *
+     * <p>Unbundled apps included in the system image are expected to detect and triage their own
+     * {@link StrictMode} issues separate from the OS release process, which is why we don't enable
+     * them here.
+     *
+     * @hide
+     */
+    public static boolean isBundledSystemApp(ApplicationInfo ai) {
+        if (ai == null || ai.packageName == null) {
+            // Probably system server
+            return true;
+        } else if (ai.isSystemApp()) {
+            // Ignore unbundled apps living in the wrong namespace
+            if (ai.packageName.equals("com.android.vending")
+                    || ai.packageName.equals("com.android.chrome")) {
+                return false;
+            }
+
+            // Ignore bundled apps that are way too spammy
+            // STOPSHIP: burn this list down to zero
+            if (ai.packageName.equals("com.android.phone")) {
+                return false;
+            }
+
+            if (ai.packageName.equals("android")
+                    || ai.packageName.startsWith("android.")
+                    || ai.packageName.startsWith("com.android.")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Initialize default {@link ThreadPolicy} for the current thread.
+     *
+     * @hide
+     */
+    public static void initThreadDefaults(ApplicationInfo ai) {
+        final ThreadPolicy.Builder builder = new ThreadPolicy.Builder();
+        final int targetSdkVersion =
+                (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        // Starting in HC, we don't allow network usage on the main thread
+        if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
+            builder.detectNetwork();
+            builder.penaltyDeathOnNetwork();
+        }
+
+        if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) {
+            // Detect nothing extra
+        } else if (Build.IS_USERDEBUG) {
+            // Detect everything in bundled apps
+            if (isBundledSystemApp(ai)) {
+                builder.detectAll();
+                builder.penaltyDropBox();
+                if (SystemProperties.getBoolean(VISUAL_PROPERTY, false)) {
+                    builder.penaltyFlashScreen();
+                }
+            }
+        } else if (Build.IS_ENG) {
+            // Detect everything in bundled apps
+            if (isBundledSystemApp(ai)) {
+                builder.detectAll();
+                builder.penaltyDropBox();
+                builder.penaltyLog();
+                builder.penaltyFlashScreen();
+            }
+        }
+
+        setThreadPolicy(builder.build());
+    }
+
+    /**
+     * Initialize default {@link VmPolicy} for the current VM.
+     *
+     * @hide
+     */
+    public static void initVmDefaults(ApplicationInfo ai) {
+        final VmPolicy.Builder builder = new VmPolicy.Builder();
+        final int targetSdkVersion =
+                (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        // Starting in N, we don't allow file:// Uri exposure
+        if (targetSdkVersion >= Build.VERSION_CODES.N) {
+            builder.detectFileUriExposure();
+            builder.penaltyDeathOnFileUriExposure();
+        }
+
+        if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) {
+            // Detect nothing extra
+        } else if (Build.IS_USERDEBUG) {
+            // Detect everything in bundled apps (except activity leaks, which
+            // are expensive to track)
+            if (isBundledSystemApp(ai)) {
+                builder.detectAll();
+                builder.permitActivityLeaks();
+                builder.penaltyDropBox();
+            }
+        } else if (Build.IS_ENG) {
+            // Detect everything in bundled apps
+            if (isBundledSystemApp(ai)) {
+                builder.detectAll();
+                builder.penaltyDropBox();
+                builder.penaltyLog();
+            }
+        }
+
+        setVmPolicy(builder.build());
+    }
+
+    /**
+     * Used by the framework to make file usage a fatal error.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void enableDeathOnFileUriExposure() {
+        sVmPolicy =
+                new VmPolicy(
+                        sVmPolicy.mask
+                                | DETECT_VM_FILE_URI_EXPOSURE
+                                | PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
+                        sVmPolicy.classInstanceLimit,
+                        sVmPolicy.mListener,
+                        sVmPolicy.mCallbackExecutor);
+    }
+
+    /**
+     * Used by lame internal apps that haven't done the hard work to get themselves off file:// Uris
+     * yet.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void disableDeathOnFileUriExposure() {
+        sVmPolicy =
+                new VmPolicy(
+                        sVmPolicy.mask
+                                & ~(DETECT_VM_FILE_URI_EXPOSURE
+                                        | PENALTY_DEATH_ON_FILE_URI_EXPOSURE),
+                        sVmPolicy.classInstanceLimit,
+                        sVmPolicy.mListener,
+                        sVmPolicy.mCallbackExecutor);
+    }
+
+    @UnsupportedAppUsage
+    private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed =
+            new ThreadLocal<ArrayList<ViolationInfo>>() {
+                @Override
+                protected ArrayList<ViolationInfo> initialValue() {
+                    return new ArrayList<ViolationInfo>();
+                }
+            };
+
+    // Note: only access this once verifying the thread has a Looper.
+    private static final ThreadLocal<Handler> THREAD_HANDLER =
+            new ThreadLocal<Handler>() {
+                @Override
+                protected Handler initialValue() {
+                    return new Handler();
+                }
+            };
+
+    private static final ThreadLocal<AndroidBlockGuardPolicy> THREAD_ANDROID_POLICY =
+            new ThreadLocal<AndroidBlockGuardPolicy>() {
+                @Override
+                protected AndroidBlockGuardPolicy initialValue() {
+                    return new AndroidBlockGuardPolicy(0);
+                }
+            };
+
+    private static boolean tooManyViolationsThisLoop() {
+        return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP;
+    }
+
+    private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
+        private @ThreadPolicyMask int mThreadPolicyMask;
+
+        // Map from violation stacktrace hashcode -> uptimeMillis of
+        // last violation.  No locking needed, as this is only
+        // accessed by the same thread.
+        /** Temporarily retained; appears to be missing UnsupportedAppUsage annotation */
+        private ArrayMap<Integer, Long> mLastViolationTime;
+        private SparseLongArray mRealLastViolationTime;
+
+        public AndroidBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
+            mThreadPolicyMask = threadPolicyMask;
+        }
+
+        @Override
+        public String toString() {
+            return "AndroidBlockGuardPolicy; mPolicyMask=" + mThreadPolicyMask;
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public int getPolicyMask() {
+            return mThreadPolicyMask;
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public void onWriteToDisk() {
+            if ((mThreadPolicyMask & DETECT_THREAD_DISK_WRITE) == 0) {
+                return;
+            }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
+            startHandlingViolationException(new DiskWriteViolation());
+        }
+
+        // Not part of BlockGuard.Policy; just part of StrictMode:
+        void onCustomSlowCall(String name) {
+            if ((mThreadPolicyMask & DETECT_THREAD_CUSTOM) == 0) {
+                return;
+            }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
+            startHandlingViolationException(new CustomViolation(name));
+        }
+
+        // Not part of BlockGuard.Policy; just part of StrictMode:
+        void onResourceMismatch(Object tag) {
+            if ((mThreadPolicyMask & DETECT_THREAD_RESOURCE_MISMATCH) == 0) {
+                return;
+            }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
+            startHandlingViolationException(new ResourceMismatchViolation(tag));
+        }
+
+        // Not part of BlockGuard.Policy; just part of StrictMode:
+        public void onUnbufferedIO() {
+            if ((mThreadPolicyMask & DETECT_THREAD_UNBUFFERED_IO) == 0) {
+                return;
+            }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
+            startHandlingViolationException(new UnbufferedIoViolation());
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public void onReadFromDisk() {
+            if ((mThreadPolicyMask & DETECT_THREAD_DISK_READ) == 0) {
+                return;
+            }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
+            startHandlingViolationException(new DiskReadViolation());
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public void onNetwork() {
+            if ((mThreadPolicyMask & DETECT_THREAD_NETWORK) == 0) {
+                return;
+            }
+            if ((mThreadPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
+                throw new NetworkOnMainThreadException();
+            }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
+            startHandlingViolationException(new NetworkViolation());
+        }
+
+        // Part of BlockGuard.Policy interface:
+        public void onExplicitGc() {
+            if ((mThreadPolicyMask & DETECT_THREAD_EXPLICIT_GC) == 0) {
+                return;
+            }
+            if (tooManyViolationsThisLoop()) {
+                return;
+            }
+            startHandlingViolationException(new ExplicitGcViolation());
+        }
+
+        public @ThreadPolicyMask int getThreadPolicyMask() {
+            return mThreadPolicyMask;
+        }
+
+        public void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
+            mThreadPolicyMask = threadPolicyMask;
+        }
+
+        // Start handling a violation that just started and hasn't
+        // actually run yet (e.g. no disk write or network operation
+        // has yet occurred).  This sees if we're in an event loop
+        // thread and, if so, uses it to roughly measure how long the
+        // violation took.
+        void startHandlingViolationException(Violation e) {
+            final int penaltyMask = (mThreadPolicyMask & PENALTY_ALL);
+            final ViolationInfo info = new ViolationInfo(e, penaltyMask);
+            info.violationUptimeMillis = SystemClock.uptimeMillis();
+            handleViolationWithTimingAttempt(info);
+        }
+
+        // Attempts to fill in the provided ViolationInfo's
+        // durationMillis field if this thread has a Looper we can use
+        // to measure with.  We measure from the time of violation
+        // until the time the looper is idle again (right before
+        // the next epoll_wait)
+        void handleViolationWithTimingAttempt(final ViolationInfo info) {
+            Looper looper = Looper.myLooper();
+
+            // Without a Looper, we're unable to time how long the
+            // violation takes place.  This case should be rare, as
+            // most users will care about timing violations that
+            // happen on their main UI thread.  Note that this case is
+            // also hit when a violation takes place in a Binder
+            // thread, in "gather" mode.  In this case, the duration
+            // of the violation is computed by the ultimate caller and
+            // its Looper, if any.
+            //
+            // Also, as a special short-cut case when the only penalty
+            // bit is death, we die immediately, rather than timing
+            // the violation's duration.  This makes it convenient to
+            // use in unit tests too, rather than waiting on a Looper.
+            //
+            // TODO: if in gather mode, ignore Looper.myLooper() and always
+            //       go into this immediate mode?
+            if (looper == null || (info.mPenaltyMask == PENALTY_DEATH)) {
+                info.durationMillis = -1; // unknown (redundant, already set)
+                onThreadPolicyViolation(info);
+                return;
+            }
+
+            final ArrayList<ViolationInfo> records = violationsBeingTimed.get();
+            if (records.size() >= MAX_OFFENSES_PER_LOOP) {
+                // Not worth measuring.  Too many offenses in one loop.
+                return;
+            }
+            records.add(info);
+            if (records.size() > 1) {
+                // There's already been a violation this loop, so we've already
+                // registered an idle handler to process the list of violations
+                // at the end of this Looper's loop.
+                return;
+            }
+
+            final IWindowManager windowManager =
+                    info.penaltyEnabled(PENALTY_FLASH) ? sWindowManager.get() : null;
+            if (windowManager != null) {
+                try {
+                    windowManager.showStrictModeViolation(true);
+                } catch (RemoteException unused) {
+                }
+            }
+
+            // We post a runnable to a Handler (== delay 0 ms) for
+            // measuring the end time of a violation instead of using
+            // an IdleHandler (as was previously used) because an
+            // IdleHandler may not run for quite a long period of time
+            // if an ongoing animation is happening and continually
+            // posting ASAP (0 ms) animation steps.  Animations are
+            // throttled back to 60fps via SurfaceFlinger/View
+            // invalidates, _not_ by posting frame updates every 16
+            // milliseconds.
+            THREAD_HANDLER
+                    .get()
+                    .postAtFrontOfQueue(
+                            () -> {
+                                long loopFinishTime = SystemClock.uptimeMillis();
+
+                                // Note: we do this early, before handling the
+                                // violation below, as handling the violation
+                                // may include PENALTY_DEATH and we don't want
+                                // to keep the red border on.
+                                if (windowManager != null) {
+                                    try {
+                                        windowManager.showStrictModeViolation(false);
+                                    } catch (RemoteException unused) {
+                                    }
+                                }
+
+                                for (int n = 0; n < records.size(); ++n) {
+                                    ViolationInfo v = records.get(n);
+                                    v.violationNumThisLoop = n + 1;
+                                    v.durationMillis =
+                                            (int) (loopFinishTime - v.violationUptimeMillis);
+                                    onThreadPolicyViolation(v);
+                                }
+                                records.clear();
+                            });
+        }
+
+        // Note: It's possible (even quite likely) that the
+        // thread-local policy mask has changed from the time the
+        // violation fired and now (after the violating code ran) due
+        // to people who push/pop temporary policy in regions of code,
+        // hence the policy being passed around.
+        void onThreadPolicyViolation(final ViolationInfo info) {
+            if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; penalty=" + info.mPenaltyMask);
+
+            if (info.penaltyEnabled(PENALTY_GATHER)) {
+                ArrayList<ViolationInfo> violations = gatheredViolations.get();
+                if (violations == null) {
+                    violations = new ArrayList<>(1);
+                    gatheredViolations.set(violations);
+                }
+                for (ViolationInfo previous : violations) {
+                    if (info.getStackTrace().equals(previous.getStackTrace())) {
+                        // Duplicate. Don't log.
+                        return;
+                    }
+                }
+                violations.add(info);
+                return;
+            }
+
+            // Not perfect, but fast and good enough for dup suppression.
+            Integer crashFingerprint = info.hashCode();
+            long lastViolationTime = 0;
+            long now = SystemClock.uptimeMillis();
+            if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+                if (mRealLastViolationTime != null) {
+                    Long vtime = mRealLastViolationTime.get(crashFingerprint);
+                    if (vtime != null) {
+                        lastViolationTime = vtime;
+                    }
+                    clampViolationTimeMap(mRealLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
+                                Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS)));
+                } else {
+                    mRealLastViolationTime = new SparseLongArray(1);
+                }
+                mRealLastViolationTime.put(crashFingerprint, now);
+            }
+            long timeSinceLastViolationMillis =
+                    lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);
+
+            if (info.penaltyEnabled(PENALTY_LOG)
+                    && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+                sLogger.log(info);
+            }
+
+            final Violation violation = info.mViolation;
+
+            // Penalties that ActivityManager should execute on our behalf.
+            int penaltyMask = 0;
+
+            if (info.penaltyEnabled(PENALTY_DIALOG)
+                    && timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) {
+                penaltyMask |= PENALTY_DIALOG;
+            }
+
+            if (info.penaltyEnabled(PENALTY_DROPBOX)
+                    && timeSinceLastViolationMillis > MIN_DROPBOX_INTERVAL_MS) {
+                penaltyMask |= PENALTY_DROPBOX;
+            }
+
+            if (penaltyMask != 0) {
+                final boolean justDropBox = (info.mPenaltyMask == PENALTY_DROPBOX);
+                if (justDropBox) {
+                    // If all we're going to ask the activity manager
+                    // to do is dropbox it (the common case during
+                    // platform development), we can avoid doing this
+                    // call synchronously which Binder data suggests
+                    // isn't always super fast, despite the implementation
+                    // in the ActivityManager trying to be mostly async.
+                    dropboxViolationAsync(penaltyMask, info);
+                } else {
+                    handleApplicationStrictModeViolation(penaltyMask, info);
+                }
+            }
+
+            if (info.penaltyEnabled(PENALTY_DEATH)) {
+                throw new RuntimeException("StrictMode ThreadPolicy violation", violation);
+            }
+
+            // penaltyDeath will cause penaltyCallback to no-op since we cannot guarantee the
+            // executor finishes before crashing.
+            final OnThreadViolationListener listener = sThreadViolationListener.get();
+            final Executor executor = sThreadViolationExecutor.get();
+            if (listener != null && executor != null) {
+                try {
+                    executor.execute(
+                            () -> {
+                                // Lift violated policy to prevent infinite recursion.
+                                ThreadPolicy oldPolicy = StrictMode.allowThreadViolations();
+                                try {
+                                    listener.onThreadViolation(violation);
+                                } finally {
+                                    StrictMode.setThreadPolicy(oldPolicy);
+                                }
+                            });
+                } catch (RejectedExecutionException e) {
+                    Log.e(TAG, "ThreadPolicy penaltyCallback failed", e);
+                }
+            }
+        }
+    }
+
+    private static final BlockGuard.VmPolicy VM_ANDROID_POLICY = new BlockGuard.VmPolicy() {
+        @Override
+        public void onPathAccess(String path) {
+            if (path == null) return;
+
+            // NOTE: keep credential-protected paths in sync with Environment.java
+            if (path.startsWith("/data/user/")
+                    || path.startsWith("/data/media/")
+                    || path.startsWith("/data/system_ce/")
+                    || path.startsWith("/data/misc_ce/")
+                    || path.startsWith("/data/vendor_ce/")
+                    || path.startsWith("/storage/emulated/")) {
+                final int second = path.indexOf('/', 1);
+                final int third = path.indexOf('/', second + 1);
+                final int fourth = path.indexOf('/', third + 1);
+                if (fourth == -1) return;
+
+                try {
+                    final int userId = Integer.parseInt(path.substring(third + 1, fourth));
+                    onCredentialProtectedPathAccess(path, userId);
+                } catch (NumberFormatException ignored) {
+                }
+            } else if (path.startsWith("/data/data/")) {
+                onCredentialProtectedPathAccess(path, UserHandle.USER_SYSTEM);
+            }
+        }
+    };
+
+    /**
+     * In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any
+     * violations but not showing a dialog, not loggging, and not killing the process. In these
+     * cases we don't need to do a synchronous call to the ActivityManager. This is used by both
+     * per-thread and vm-wide violations when applicable.
+     */
+    private static void dropboxViolationAsync(
+            final int penaltyMask, final ViolationInfo info) {
+        int outstanding = sDropboxCallsInFlight.incrementAndGet();
+        if (outstanding > 20) {
+            // What's going on?  Let's not make make the situation
+            // worse and just not log.
+            sDropboxCallsInFlight.decrementAndGet();
+            return;
+        }
+
+        if (LOG_V) Log.d(TAG, "Dropboxing async; in-flight=" + outstanding);
+
+        BackgroundThread.getHandler().post(() -> {
+            handleApplicationStrictModeViolation(penaltyMask, info);
+            int outstandingInner = sDropboxCallsInFlight.decrementAndGet();
+            if (LOG_V) Log.d(TAG, "Dropbox complete; in-flight=" + outstandingInner);
+        });
+    }
+
+    private static void handleApplicationStrictModeViolation(int penaltyMask,
+            ViolationInfo info) {
+        final int oldMask = getThreadPolicyMask();
+        try {
+            // First, remove any policy before we call into the Activity Manager,
+            // otherwise we'll infinite recurse as we try to log policy violations
+            // to disk, thus violating policy, thus requiring logging, etc...
+            // We restore the current policy below, in the finally block.
+            setThreadPolicyMask(0);
+
+            IActivityManager am = ActivityManager.getService();
+            if (am == null) {
+                Log.w(TAG, "No activity manager; failed to Dropbox violation.");
+            } else {
+                am.handleApplicationStrictModeViolation(
+                        RuntimeInit.getApplicationObject(), penaltyMask, info);
+            }
+        } catch (RemoteException e) {
+            if (e instanceof DeadObjectException) {
+                // System process is dead; ignore
+            } else {
+                Log.e(TAG, "RemoteException handling StrictMode violation", e);
+            }
+        } finally {
+            setThreadPolicyMask(oldMask);
+        }
+    }
+
+    private static class AndroidCloseGuardReporter implements CloseGuard.Reporter {
+
+        @Override
+        public void report(String message, Throwable allocationSite) {
+            onVmPolicyViolation(new LeakedClosableViolation(message, allocationSite));
+        }
+
+        @Override
+        public void report(String message) {
+            onVmPolicyViolation(new LeakedClosableViolation(message));
+        }
+    }
+
+    /** Called from Parcel.writeNoException() */
+    /* package */ static boolean hasGatheredViolations() {
+        return gatheredViolations.get() != null;
+    }
+
+    /**
+     * Called from Parcel.writeException(), so we drop this memory and don't incorrectly attribute
+     * it to the wrong caller on the next Binder call on this thread.
+     */
+    /* package */ static void clearGatheredViolations() {
+        gatheredViolations.set(null);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    @TestApi
+    public static void conditionallyCheckInstanceCounts() {
+        VmPolicy policy = getVmPolicy();
+        int policySize = policy.classInstanceLimit.size();
+        if (policySize == 0) {
+            return;
+        }
+
+        System.gc();
+        System.runFinalization();
+        System.gc();
+
+        // Note: classInstanceLimit is immutable, so this is lock-free
+        // Create the classes array.
+        Class[] classes = policy.classInstanceLimit.keySet().toArray(new Class[policySize]);
+        long[] instanceCounts = VMDebug.countInstancesOfClasses(classes, false);
+        for (int i = 0; i < classes.length; ++i) {
+            Class klass = classes[i];
+            int limit = policy.classInstanceLimit.get(klass);
+            long instances = instanceCounts[i];
+            if (instances > limit) {
+                onVmPolicyViolation(new InstanceCountViolation(klass, instances, limit));
+            }
+        }
+    }
+
+    private static long sLastInstanceCountCheckMillis = 0;
+    private static boolean sIsIdlerRegistered = false; // guarded by StrictMode.class
+    private static final MessageQueue.IdleHandler sProcessIdleHandler =
+            new MessageQueue.IdleHandler() {
+                public boolean queueIdle() {
+                    long now = SystemClock.uptimeMillis();
+                    if (now - sLastInstanceCountCheckMillis > 30 * 1000) {
+                        sLastInstanceCountCheckMillis = now;
+                        conditionallyCheckInstanceCounts();
+                    }
+                    return true;
+                }
+            };
+
+    /**
+     * Sets the policy for what actions in the VM process (on any thread) should be detected, as
+     * well as the penalty if such actions occur.
+     *
+     * @param policy the policy to put into place
+     */
+    public static void setVmPolicy(final VmPolicy policy) {
+        synchronized (StrictMode.class) {
+            sVmPolicy = policy;
+            setCloseGuardEnabled(vmClosableObjectLeaksEnabled());
+
+            Looper looper = Looper.getMainLooper();
+            if (looper != null) {
+                MessageQueue mq = looper.mQueue;
+                if (policy.classInstanceLimit.size() == 0
+                        || (sVmPolicy.mask & PENALTY_ALL) == 0) {
+                    mq.removeIdleHandler(sProcessIdleHandler);
+                    sIsIdlerRegistered = false;
+                } else if (!sIsIdlerRegistered) {
+                    mq.addIdleHandler(sProcessIdleHandler);
+                    sIsIdlerRegistered = true;
+                }
+            }
+
+            int networkPolicy = NETWORK_POLICY_ACCEPT;
+            if ((sVmPolicy.mask & DETECT_VM_CLEARTEXT_NETWORK) != 0) {
+                if ((sVmPolicy.mask & PENALTY_DEATH) != 0
+                        || (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0) {
+                    networkPolicy = NETWORK_POLICY_REJECT;
+                } else {
+                    networkPolicy = NETWORK_POLICY_LOG;
+                }
+            }
+
+            final INetworkManagementService netd =
+                    INetworkManagementService.Stub.asInterface(
+                            ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+            if (netd != null) {
+                try {
+                    netd.setUidCleartextNetworkPolicy(android.os.Process.myUid(), networkPolicy);
+                } catch (RemoteException ignored) {
+                }
+            } else if (networkPolicy != NETWORK_POLICY_ACCEPT) {
+                Log.w(TAG, "Dropping requested network policy due to missing service!");
+            }
+
+
+            if ((sVmPolicy.mask & DETECT_VM_NON_SDK_API_USAGE) != 0) {
+                VMRuntime.setNonSdkApiUsageConsumer(sNonSdkApiUsageConsumer);
+                VMRuntime.setDedupeHiddenApiWarnings(false);
+            } else {
+                VMRuntime.setNonSdkApiUsageConsumer(null);
+                VMRuntime.setDedupeHiddenApiWarnings(true);
+            }
+
+            if ((sVmPolicy.mask & DETECT_VM_UNSAFE_INTENT_LAUNCH) != 0) {
+                registerIntentMatchingRestrictionCallback();
+            }
+
+            setBlockGuardVmPolicy(sVmPolicy.mask);
+        }
+    }
+
+    private static void registerIntentMatchingRestrictionCallback() {
+        try {
+            ActivityManager.getService().registerStrictModeCallback(
+                    new UnsafeIntentStrictModeCallback());
+        } catch (RemoteException e) {
+            /*
+            If exception is DeadObjectException it means system process is dead, so we can ignore
+             */
+            if (!(e instanceof DeadObjectException)) {
+                Log.e(TAG, "RemoteException handling StrictMode violation", e);
+            }
+        }
+    }
+
+    private static final class UnsafeIntentStrictModeCallback
+            extends IUnsafeIntentStrictModeCallback.Stub {
+        @Override
+        public void onImplicitIntentMatchedInternalComponent(Intent intent) {
+            if (StrictMode.vmUnsafeIntentLaunchEnabled()) {
+                StrictMode.onUnsafeIntentLaunch(intent,
+                        "Launch of unsafe implicit intent: " + intent);
+            }
+        }
+    }
+
+    /** Gets the current VM policy. */
+    public static VmPolicy getVmPolicy() {
+        synchronized (StrictMode.class) {
+            return sVmPolicy;
+        }
+    }
+
+    /**
+     * Enable the recommended StrictMode defaults, with violations just being logged.
+     *
+     * <p>This catches disk and network access on the main thread, as well as leaked SQLite cursors
+     * and unclosed resources. This is simply a wrapper around {@link #setVmPolicy} and {@link
+     * #setThreadPolicy}.
+     */
+    public static void enableDefaults() {
+        setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+        setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
+    }
+
+    /** @hide */
+    public static boolean vmSqliteObjectLeaksEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_CURSOR_LEAKS) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmClosableObjectLeaksEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_CLOSABLE_LEAKS) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmRegistrationLeaksEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_REGISTRATION_LEAKS) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmFileUriExposureEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_FILE_URI_EXPOSURE) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmCleartextNetworkEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_CLEARTEXT_NETWORK) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmContentUriWithoutPermissionEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmUntaggedSocketEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_UNTAGGED_SOCKET) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmImplicitDirectBootEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_IMPLICIT_DIRECT_BOOT) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmCredentialProtectedWhileLockedEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmIncorrectContextUseEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_INCORRECT_CONTEXT_USE) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmUnsafeIntentLaunchEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_UNSAFE_INTENT_LAUNCH) != 0;
+    }
+
+    /** @hide */
+    public static void onSqliteObjectLeaked(String message, Throwable originStack) {
+        onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
+        onVmPolicyViolation(new WebViewMethodCalledOnWrongThreadViolation(originStack));
+    }
+
+    /** @hide */
+    public static void onIntentReceiverLeaked(Throwable originStack) {
+        onVmPolicyViolation(new IntentReceiverLeakedViolation(originStack));
+    }
+
+    /** @hide */
+    public static void onServiceConnectionLeaked(Throwable originStack) {
+        onVmPolicyViolation(new ServiceConnectionLeakedViolation(originStack));
+    }
+
+    /** @hide */
+    public static void onFileUriExposed(Uri uri, String location) {
+        final String message = uri + " exposed beyond app through " + location;
+        if ((sVmPolicy.mask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) {
+            throw new FileUriExposedException(message);
+        } else {
+            onVmPolicyViolation(new FileUriExposedViolation(message));
+        }
+    }
+
+    /** @hide */
+    public static void onContentUriWithoutPermission(Uri uri, String location) {
+        onVmPolicyViolation(new ContentUriWithoutPermissionViolation(uri, location));
+    }
+
+    /** @hide */
+    public static void onCleartextNetworkDetected(byte[] firstPacket) {
+        byte[] rawAddr = null;
+        if (firstPacket != null) {
+            if (firstPacket.length >= 20 && (firstPacket[0] & 0xf0) == 0x40) {
+                // IPv4
+                rawAddr = new byte[4];
+                System.arraycopy(firstPacket, 16, rawAddr, 0, 4);
+            } else if (firstPacket.length >= 40 && (firstPacket[0] & 0xf0) == 0x60) {
+                // IPv6
+                rawAddr = new byte[16];
+                System.arraycopy(firstPacket, 24, rawAddr, 0, 16);
+            }
+        }
+
+        final int uid = android.os.Process.myUid();
+        final StringBuilder msg = new StringBuilder("Detected cleartext network traffic from UID ")
+                .append(uid);
+        if (rawAddr != null) {
+            try {
+                msg.append(" to ").append(InetAddress.getByAddress(rawAddr));
+            } catch (UnknownHostException ignored) {
+            }
+        }
+        msg.append(HexDump.dumpHexString(firstPacket).trim()).append(' ');
+        final boolean forceDeath = (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0;
+        onVmPolicyViolation(new CleartextNetworkViolation(msg.toString()), forceDeath);
+    }
+
+    /** @hide */
+    public static void onUntaggedSocket() {
+        onVmPolicyViolation(new UntaggedSocketViolation());
+    }
+
+    /** @hide */
+    public static void onImplicitDirectBoot() {
+        onVmPolicyViolation(new ImplicitDirectBootViolation());
+    }
+
+    /** @hide */
+    public static void onIncorrectContextUsed(String message, Throwable originStack) {
+        onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack));
+    }
+
+    /**
+     * A helper method to verify if the {@code context} has a proper {@link Configuration} to obtain
+     * {@link android.view.LayoutInflater}, {@link android.view.ViewConfiguration} or
+     * {@link android.view.GestureDetector}. Throw {@link IncorrectContextUseViolation} if the
+     * {@code context} doesn't have a proper configuration.
+     * <p>
+     * Note that the context created via {@link Context#createConfigurationContext(Configuration)}
+     * is also regarded as a context with a proper configuration because the {@link Configuration}
+     * is handled by developers.
+     * </p>
+     * @param context The context to verify if it is a display associative context
+     * @param methodName The asserted method name
+     *
+     * @see Context#isConfigurationContext()
+     * @see Context#createConfigurationContext(Configuration)
+     * @see Context#getSystemService(String)
+     * @see Context#LAYOUT_INFLATER_SERVICE
+     * @see android.view.ViewConfiguration#get(Context)
+     * @see android.view.LayoutInflater#from(Context)
+     * @see IncorrectContextUseViolation
+     *
+     * @hide
+     */
+    public static void assertConfigurationContext(@NonNull Context context,
+            @NonNull String methodName) {
+        if (vmIncorrectContextUseEnabled() && !context.isConfigurationContext()) {
+            final String errorMessage = "Tried to access the API:" + methodName + " which needs to"
+                    + " have proper configuration from a non-UI Context:" + context;
+            final String message = "The API:" + methodName + " needs a proper configuration."
+                    + " Use UI contexts such as an activity or a context created"
+                    + " via createWindowContext(Display, int, Bundle) or "
+                    + " createConfigurationContext(Configuration) with a proper configuration.";
+            final Exception exception = new IllegalAccessException(errorMessage);
+            StrictMode.onIncorrectContextUsed(message, exception);
+            Log.e(TAG, errorMessage + " " + message, exception);
+        }
+    }
+
+    /**
+     * A helper method to verify if the {@code context} is a UI context and throw
+     * {@link IncorrectContextUseViolation} if the {@code context} is not a UI context.
+     *
+     * @param context The context to verify if it is a UI context
+     * @param methodName The asserted method name
+     *
+     * @see Context#isUiContext()
+     * @see IncorrectContextUseViolation
+     *
+     * @hide
+     */
+    public static void assertUiContext(@NonNull Context context, @NonNull String methodName) {
+        if (vmIncorrectContextUseEnabled() && !context.isUiContext()) {
+            final String errorMessage = "Tried to access UI related API:" + methodName
+                    + " from a non-UI Context:" + context;
+            final String message = methodName + " should be accessed from Activity or other UI "
+                    + "Contexts. Use an Activity or a Context created with "
+                    + "Context#createWindowContext(int, Bundle), which are adjusted to "
+                    + "the configuration and visual bounds of an area on screen.";
+            final Exception exception = new IllegalAccessException(errorMessage);
+            StrictMode.onIncorrectContextUsed(message, exception);
+            Log.e(TAG, errorMessage + " " + message, exception);
+        }
+    }
+
+    /** @hide */
+    public static void onUnsafeIntentLaunch(Intent intent) {
+        onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent));
+    }
+
+    /** @hide */
+    public static void onUnsafeIntentLaunch(Intent intent, String message) {
+        onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent, message));
+    }
+
+    /** Assume locked until we hear otherwise */
+    private static volatile boolean sUserKeyUnlocked = false;
+
+    private static boolean isUserKeyUnlocked(int userId) {
+        final IStorageManager storage = IStorageManager.Stub
+                .asInterface(ServiceManager.getService("mount"));
+        if (storage != null) {
+            try {
+                return storage.isUserKeyUnlocked(userId);
+            } catch (RemoteException ignored) {
+            }
+        }
+        return false;
+    }
+
+    /** @hide */
+    private static void onCredentialProtectedPathAccess(String path, int userId) {
+        // We can cache the unlocked state for the userId we're running as,
+        // since any relocking of that user will always result in our
+        // process being killed to release any CE FDs we're holding onto.
+        if (userId == UserHandle.myUserId()) {
+            if (sUserKeyUnlocked) {
+                return;
+            } else if (isUserKeyUnlocked(userId)) {
+                sUserKeyUnlocked = true;
+                return;
+            }
+        } else if (isUserKeyUnlocked(userId)) {
+            return;
+        }
+
+        onVmPolicyViolation(new CredentialProtectedWhileLockedViolation(
+                "Accessed credential protected path " + path + " while user " + userId
+                        + " was locked"));
+    }
+
+    // Map from VM violation fingerprint to uptime millis.
+    @UnsupportedAppUsage
+    private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();
+    private static final SparseLongArray sRealLastVmViolationTime = new SparseLongArray();
+
+    /**
+     * Clamp the given map by removing elements with timestamp older than the given retainSince.
+     */
+    private static void clampViolationTimeMap(final @NonNull SparseLongArray violationTime,
+            final long retainSince) {
+        for (int i = 0; i < violationTime.size(); ) {
+            if (violationTime.valueAt(i) < retainSince) {
+                // Remove stale entries
+                violationTime.removeAt(i);
+            } else {
+                i++;
+            }
+        }
+        // Ideally we'd cap the total size of the map, though it'll involve quickselect of topK,
+        // seems not worth it (saving some space immediately but they will be obsoleted soon anyway)
+    }
+
+    /** @hide */
+    public static void onVmPolicyViolation(Violation originStack) {
+        onVmPolicyViolation(originStack, false);
+    }
+
+    /** @hide */
+    public static void onVmPolicyViolation(Violation violation, boolean forceDeath) {
+        final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
+        final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
+        final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
+
+        final int penaltyMask = (sVmPolicy.mask & PENALTY_ALL);
+        final ViolationInfo info = new ViolationInfo(violation, penaltyMask);
+
+        // Erase stuff not relevant for process-wide violations
+        info.numAnimationsRunning = 0;
+        info.tags = null;
+        info.broadcastIntentAction = null;
+
+        final Integer fingerprint = info.hashCode();
+        final long now = SystemClock.uptimeMillis();
+        long lastViolationTime;
+        long timeSinceLastViolationMillis = Long.MAX_VALUE;
+        if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+            synchronized (sRealLastVmViolationTime) {
+                if (sRealLastVmViolationTime.indexOfKey(fingerprint) >= 0) {
+                    lastViolationTime = sRealLastVmViolationTime.get(fingerprint);
+                    timeSinceLastViolationMillis = now - lastViolationTime;
+                }
+                if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
+                    sRealLastVmViolationTime.put(fingerprint, now);
+                }
+                clampViolationTimeMap(sRealLastVmViolationTime,
+                        now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS));
+            }
+        }
+        if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
+            // Rate limit all penalties.
+            return;
+        }
+
+        if (penaltyLog && sLogger != null && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+            sLogger.log(info);
+        }
+
+        if (penaltyDropbox) {
+            if (penaltyDeath) {
+                handleApplicationStrictModeViolation(PENALTY_DROPBOX, info);
+            } else {
+                // Common case for userdebug/eng builds.  If no death and
+                // just dropboxing, we can do the ActivityManager call
+                // asynchronously.
+                dropboxViolationAsync(PENALTY_DROPBOX, info);
+            }
+        }
+
+        if (penaltyDeath) {
+            System.err.println("StrictMode VmPolicy violation with POLICY_DEATH; shutting down.");
+            Process.killProcess(Process.myPid());
+            System.exit(10);
+        }
+
+        // If penaltyDeath, we can't guarantee this callback finishes before the process dies for
+        // all executors. penaltyDeath supersedes penaltyCallback.
+        if (sVmPolicy.mListener != null && sVmPolicy.mCallbackExecutor != null) {
+            final OnVmViolationListener listener = sVmPolicy.mListener;
+            try {
+                sVmPolicy.mCallbackExecutor.execute(
+                        () -> {
+                            // Lift violated policy to prevent infinite recursion.
+                            VmPolicy oldPolicy = allowVmViolations();
+                            try {
+                                listener.onVmViolation(violation);
+                            } finally {
+                                setVmPolicy(oldPolicy);
+                            }
+                        });
+            } catch (RejectedExecutionException e) {
+                Log.e(TAG, "VmPolicy penaltyCallback failed", e);
+            }
+        }
+    }
+
+    /** Called from Parcel.writeNoException() */
+    /* package */ static void writeGatheredViolationsToParcel(Parcel p) {
+        ArrayList<ViolationInfo> violations = gatheredViolations.get();
+        if (violations == null) {
+            p.writeInt(0);
+        } else {
+            // To avoid taking up too much transaction space, only include
+            // details for the first 3 violations. Deep inside, CrashInfo
+            // will truncate each stack trace to ~20kB.
+            final int size = Math.min(violations.size(), 3);
+            p.writeInt(size);
+            for (int i = 0; i < size; i++) {
+                violations.get(i).writeToParcel(p, 0);
+            }
+        }
+        gatheredViolations.set(null);
+    }
+
+    /**
+     * Called from Parcel.readException() when the exception is EX_STRICT_MODE_VIOLATIONS, we here
+     * read back all the encoded violations.
+     */
+    /* package */ static void readAndHandleBinderCallViolations(Parcel p) {
+        Throwable localCallSite = new Throwable();
+        final int policyMask = getThreadPolicyMask();
+        final boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0;
+
+        final int size = p.readInt();
+        for (int i = 0; i < size; i++) {
+            final ViolationInfo info = new ViolationInfo(p, !currentlyGathering);
+            info.addLocalStack(localCallSite);
+            BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+            if (policy instanceof AndroidBlockGuardPolicy) {
+                ((AndroidBlockGuardPolicy) policy).handleViolationWithTimingAttempt(info);
+            }
+        }
+    }
+
+    /**
+     * Called from android_util_Binder.cpp's android_os_Parcel_enforceInterface when an incoming
+     * Binder call requires changing the StrictMode policy mask. The role of this function is to ask
+     * Binder for its current (native) thread-local policy value and synchronize it to libcore's
+     * (Java) thread-local policy value.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private static void onBinderStrictModePolicyChange(@ThreadPolicyMask int newPolicy) {
+        setBlockGuardPolicy(newPolicy);
+    }
+
+    /**
+     * A tracked, critical time span. (e.g. during an animation.)
+     *
+     * <p>The object itself is a linked list node, to avoid any allocations during rapid span
+     * entries and exits.
+     *
+     * @hide
+     */
+    public static class Span {
+        private String mName;
+        private long mCreateMillis;
+        private Span mNext;
+        private Span mPrev; // not used when in freeList, only active
+        private final ThreadSpanState mContainerState;
+
+        Span(ThreadSpanState threadState) {
+            mContainerState = threadState;
+        }
+
+        // Empty constructor for the NO_OP_SPAN
+        protected Span() {
+            mContainerState = null;
+        }
+
+        /**
+         * To be called when the critical span is complete (i.e. the animation is done animating).
+         * This can be called on any thread (even a different one from where the animation was
+         * taking place), but that's only a defensive implementation measure. It really makes no
+         * sense for you to call this on thread other than that where you created it.
+         *
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public void finish() {
+            ThreadSpanState state = mContainerState;
+            synchronized (state) {
+                if (mName == null) {
+                    // Duplicate finish call.  Ignore.
+                    return;
+                }
+
+                // Remove ourselves from the active list.
+                if (mPrev != null) {
+                    mPrev.mNext = mNext;
+                }
+                if (mNext != null) {
+                    mNext.mPrev = mPrev;
+                }
+                if (state.mActiveHead == this) {
+                    state.mActiveHead = mNext;
+                }
+
+                state.mActiveSize--;
+
+                if (LOG_V) Log.d(TAG, "Span finished=" + mName + "; size=" + state.mActiveSize);
+
+                this.mCreateMillis = -1;
+                this.mName = null;
+                this.mPrev = null;
+                this.mNext = null;
+
+                // Add ourselves to the freeList, if it's not already
+                // too big.
+                if (state.mFreeListSize < 5) {
+                    this.mNext = state.mFreeListHead;
+                    state.mFreeListHead = this;
+                    state.mFreeListSize++;
+                }
+            }
+        }
+    }
+
+    // The no-op span that's used in user builds.
+    private static final Span NO_OP_SPAN =
+            new Span() {
+                public void finish() {
+                    // Do nothing.
+                }
+            };
+
+    /**
+     * Linked lists of active spans and a freelist.
+     *
+     * <p>Locking notes: there's one of these structures per thread and all members of this
+     * structure (as well as the Span nodes under it) are guarded by the ThreadSpanState object
+     * instance. While in theory there'd be no locking required because it's all local per-thread,
+     * the finish() method above is defensive against people calling it on a different thread from
+     * where they created the Span, hence the locking.
+     */
+    private static class ThreadSpanState {
+        public Span mActiveHead; // doubly-linked list.
+        public int mActiveSize;
+        public Span mFreeListHead; // singly-linked list.  only changes at head.
+        public int mFreeListSize;
+    }
+
+    private static final ThreadLocal<ThreadSpanState> sThisThreadSpanState =
+            new ThreadLocal<ThreadSpanState>() {
+                @Override
+                protected ThreadSpanState initialValue() {
+                    return new ThreadSpanState();
+                }
+            };
+
+    @UnsupportedAppUsage
+    private static Singleton<IWindowManager> sWindowManager =
+            new Singleton<IWindowManager>() {
+                protected IWindowManager create() {
+                    return IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+                }
+            };
+
+    /**
+     * Enter a named critical span (e.g. an animation)
+     *
+     * <p>The name is an arbitary label (or tag) that will be applied to any strictmode violation
+     * that happens while this span is active. You must call finish() on the span when done.
+     *
+     * <p>This will never return null, but on devices without debugging enabled, this may return a
+     * placeholder object on which the finish() method is a no-op.
+     *
+     * <p>TODO: add CloseGuard to this, verifying callers call finish.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static Span enterCriticalSpan(String name) {
+        if (Build.IS_USER) {
+            return NO_OP_SPAN;
+        }
+        if (name == null || name.isEmpty()) {
+            throw new IllegalArgumentException("name must be non-null and non-empty");
+        }
+        ThreadSpanState state = sThisThreadSpanState.get();
+        Span span = null;
+        synchronized (state) {
+            if (state.mFreeListHead != null) {
+                span = state.mFreeListHead;
+                state.mFreeListHead = span.mNext;
+                state.mFreeListSize--;
+            } else {
+                // Shouldn't have to do this often.
+                span = new Span(state);
+            }
+            span.mName = name;
+            span.mCreateMillis = SystemClock.uptimeMillis();
+            span.mNext = state.mActiveHead;
+            span.mPrev = null;
+            state.mActiveHead = span;
+            state.mActiveSize++;
+            if (span.mNext != null) {
+                span.mNext.mPrev = span;
+            }
+            if (LOG_V) Log.d(TAG, "Span enter=" + name + "; size=" + state.mActiveSize);
+        }
+        return span;
+    }
+
+    /**
+     * For code to note that it's slow. This is a no-op unless the current thread's {@link
+     * android.os.StrictMode.ThreadPolicy} has {@link
+     * android.os.StrictMode.ThreadPolicy.Builder#detectCustomSlowCalls} enabled.
+     *
+     * @param name a short string for the exception stack trace that's built if when this fires.
+     */
+    public static void noteSlowCall(String name) {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            // StrictMode not enabled.
+            return;
+        }
+        ((AndroidBlockGuardPolicy) policy).onCustomSlowCall(name);
+    }
+
+    /** @hide */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void noteUntaggedSocket() {
+        if (vmUntaggedSocketEnabled()) onUntaggedSocket();
+    }
+
+    /**
+     * For code to note that a resource was obtained using a type other than its defined type. This
+     * is a no-op unless the current thread's {@link android.os.StrictMode.ThreadPolicy} has {@link
+     * android.os.StrictMode.ThreadPolicy.Builder#detectResourceMismatches()} enabled.
+     *
+     * @param tag an object for the exception stack trace that's built if when this fires.
+     * @hide
+     */
+    public static void noteResourceMismatch(Object tag) {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            // StrictMode not enabled.
+            return;
+        }
+        ((AndroidBlockGuardPolicy) policy).onResourceMismatch(tag);
+    }
+
+    /** @hide */
+    public static void noteUnbufferedIO() {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            // StrictMode not enabled.
+            return;
+        }
+        policy.onUnbufferedIO();
+    }
+
+    /** @hide */
+    public static void noteDiskRead() {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            // StrictMode not enabled.
+            return;
+        }
+        policy.onReadFromDisk();
+    }
+
+    /** @hide */
+    public static void noteDiskWrite() {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            // StrictMode not enabled.
+            return;
+        }
+        policy.onWriteToDisk();
+    }
+
+    @GuardedBy("StrictMode.class")
+    private static final HashMap<Class, Integer> sExpectedActivityInstanceCount = new HashMap<>();
+
+    /**
+     * Returns an object that is used to track instances of activites. The activity should store a
+     * reference to the tracker object in one of its fields.
+     *
+     * @hide
+     */
+    public static Object trackActivity(Object instance) {
+        return new InstanceTracker(instance);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static void incrementExpectedActivityCount(Class klass) {
+        if (klass == null) {
+            return;
+        }
+
+        synchronized (StrictMode.class) {
+            if ((sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) {
+                return;
+            }
+
+            // Use the instance count from InstanceTracker as initial value.
+            Integer expected = sExpectedActivityInstanceCount.get(klass);
+            Integer newExpected =
+                    expected == null ? InstanceTracker.getInstanceCount(klass) + 1 : expected + 1;
+            sExpectedActivityInstanceCount.put(klass, newExpected);
+        }
+    }
+
+    /** @hide */
+    public static void decrementExpectedActivityCount(Class klass) {
+        if (klass == null) {
+            return;
+        }
+
+        final int limit;
+        synchronized (StrictMode.class) {
+            if ((sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) {
+                return;
+            }
+
+            Integer expected = sExpectedActivityInstanceCount.get(klass);
+            int newExpected = (expected == null || expected == 0) ? 0 : expected - 1;
+            if (newExpected == 0) {
+                sExpectedActivityInstanceCount.remove(klass);
+            } else {
+                sExpectedActivityInstanceCount.put(klass, newExpected);
+            }
+
+            // Note: adding 1 here to give some breathing room during
+            // orientation changes.  (shouldn't be necessary, though?)
+            limit = newExpected + 1;
+        }
+
+        // Quick check.
+        int actual = InstanceTracker.getInstanceCount(klass);
+        if (actual <= limit) {
+            return;
+        }
+
+        // Do a GC and explicit count to double-check.
+        // This is the work that we are trying to avoid by tracking the object instances
+        // explicity.  Running an explicit GC can be expensive (80ms) and so can walking
+        // the heap to count instance (30ms).  This extra work can make the system feel
+        // noticeably less responsive during orientation changes when activities are
+        // being restarted.  Granted, it is only a problem when StrictMode is enabled
+        // but it is annoying.
+
+        System.gc();
+        System.runFinalization();
+        System.gc();
+
+        long instances = VMDebug.countInstancesOfClass(klass, false);
+        if (instances > limit) {
+            onVmPolicyViolation(new InstanceCountViolation(klass, instances, limit));
+        }
+    }
+
+    /**
+     * Parcelable that gets sent in Binder call headers back to callers to report violations that
+     * happened during a cross-process call.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class ViolationInfo implements Parcelable {
+        /** Stack and violation details. */
+        private final Violation mViolation;
+
+        /** Path leading to a violation that occurred across binder. */
+        private final Deque<StackTraceElement[]> mBinderStack = new ArrayDeque<>();
+
+        /** Memoized stack trace of full violation. */
+        @Nullable private String mStackTrace;
+
+        /** The strict mode penalty mask at the time of violation. */
+        private final int mPenaltyMask;
+
+        /** The wall time duration of the violation, when known. -1 when not known. */
+        public int durationMillis = -1;
+
+        /** The number of animations currently running. */
+        public int numAnimationsRunning = 0;
+
+        /** List of tags from active Span instances during this violation, or null for none. */
+        public String[] tags;
+
+        /**
+         * Which violation number this was (1-based) since the last Looper loop, from the
+         * perspective of the root caller (if it crossed any processes via Binder calls). The value
+         * is 0 if the root caller wasn't on a Looper thread.
+         */
+        public int violationNumThisLoop;
+
+        /** The time (in terms of SystemClock.uptimeMillis()) that the violation occurred. */
+        public long violationUptimeMillis;
+
+        /**
+         * The action of the Intent being broadcast to somebody's onReceive on this thread right
+         * now, or null.
+         */
+        public String broadcastIntentAction;
+
+        /** If this is a instance count violation, the number of instances in memory, else -1. */
+        public long numInstances = -1;
+
+        /** Create an instance of ViolationInfo initialized from an exception. */
+        ViolationInfo(Violation tr, int penaltyMask) {
+            this.mViolation = tr;
+            this.mPenaltyMask = penaltyMask;
+            violationUptimeMillis = SystemClock.uptimeMillis();
+            this.numAnimationsRunning = ValueAnimator.getCurrentAnimationsCount();
+            Intent broadcastIntent = ActivityThread.getIntentBeingBroadcast();
+            if (broadcastIntent != null) {
+                broadcastIntentAction = broadcastIntent.getAction();
+            }
+            ThreadSpanState state = sThisThreadSpanState.get();
+            if (tr instanceof InstanceCountViolation) {
+                this.numInstances = ((InstanceCountViolation) tr).getNumberOfInstances();
+            }
+            synchronized (state) {
+                int spanActiveCount = state.mActiveSize;
+                if (spanActiveCount > MAX_SPAN_TAGS) {
+                    spanActiveCount = MAX_SPAN_TAGS;
+                }
+                if (spanActiveCount != 0) {
+                    this.tags = new String[spanActiveCount];
+                    Span iter = state.mActiveHead;
+                    int index = 0;
+                    while (iter != null && index < spanActiveCount) {
+                        this.tags[index] = iter.mName;
+                        index++;
+                        iter = iter.mNext;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Equivalent output to
+         * {@link android.app.ApplicationErrorReport.CrashInfo#stackTrace}.
+         */
+        public String getStackTrace() {
+            if (mStackTrace == null) {
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new FastPrintWriter(sw, false, 256);
+                mViolation.printStackTrace(pw);
+                for (StackTraceElement[] traces : mBinderStack) {
+                    pw.append("# via Binder call with stack:\n");
+                    for (StackTraceElement traceElement : traces) {
+                        pw.append("\tat ");
+                        pw.append(traceElement.toString());
+                        pw.append('\n');
+                    }
+                }
+                pw.flush();
+                pw.close();
+                mStackTrace = sw.toString();
+            }
+            return mStackTrace;
+        }
+
+        public Class<? extends Violation> getViolationClass() {
+            return mViolation.getClass();
+        }
+
+        /**
+         * Optional message describing this violation.
+         *
+         * @hide
+         */
+        @TestApi
+        public String getViolationDetails() {
+            return mViolation.getMessage();
+        }
+
+        boolean penaltyEnabled(int p) {
+            return (mPenaltyMask & p) != 0;
+        }
+
+        /**
+         * Add a {@link Throwable} from the current process that caused the underlying violation. We
+         * only preserve the stack trace elements.
+         *
+         * @hide
+         */
+        void addLocalStack(Throwable t) {
+            mBinderStack.addFirst(t.getStackTrace());
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 17;
+            if (mViolation != null) {
+                result = 37 * result + mViolation.hashCode();
+            }
+            if (numAnimationsRunning != 0) {
+                result *= 37;
+            }
+            if (broadcastIntentAction != null) {
+                result = 37 * result + broadcastIntentAction.hashCode();
+            }
+            if (tags != null) {
+                for (String tag : tags) {
+                    result = 37 * result + tag.hashCode();
+                }
+            }
+            return result;
+        }
+
+        /** Create an instance of ViolationInfo initialized from a Parcel. */
+        @UnsupportedAppUsage
+        public ViolationInfo(Parcel in) {
+            this(in, false);
+        }
+
+        /**
+         * Create an instance of ViolationInfo initialized from a Parcel.
+         *
+         * @param unsetGatheringBit if true, the caller is the root caller and the gathering penalty
+         *     should be removed.
+         */
+        public ViolationInfo(Parcel in, boolean unsetGatheringBit) {
+            mViolation = (Violation) in.readSerializable(android.os.strictmode.Violation.class.getClassLoader(), android.os.strictmode.Violation.class);
+            int binderStackSize = in.readInt();
+            for (int i = 0; i < binderStackSize; i++) {
+                StackTraceElement[] traceElements = new StackTraceElement[in.readInt()];
+                for (int j = 0; j < traceElements.length; j++) {
+                    StackTraceElement element =
+                            new StackTraceElement(
+                                    in.readString(),
+                                    in.readString(),
+                                    in.readString(),
+                                    in.readInt());
+                    traceElements[j] = element;
+                }
+                mBinderStack.add(traceElements);
+            }
+            int rawPenaltyMask = in.readInt();
+            if (unsetGatheringBit) {
+                mPenaltyMask = rawPenaltyMask & ~PENALTY_GATHER;
+            } else {
+                mPenaltyMask = rawPenaltyMask;
+            }
+            durationMillis = in.readInt();
+            violationNumThisLoop = in.readInt();
+            numAnimationsRunning = in.readInt();
+            violationUptimeMillis = in.readLong();
+            numInstances = in.readLong();
+            broadcastIntentAction = in.readString();
+            tags = in.readStringArray();
+        }
+
+        /** Save a ViolationInfo instance to a parcel. */
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeSerializable(mViolation);
+            dest.writeInt(mBinderStack.size());
+            for (StackTraceElement[] traceElements : mBinderStack) {
+                dest.writeInt(traceElements.length);
+                for (StackTraceElement element : traceElements) {
+                    dest.writeString(element.getClassName());
+                    dest.writeString(element.getMethodName());
+                    dest.writeString(element.getFileName());
+                    dest.writeInt(element.getLineNumber());
+                }
+            }
+            int start = dest.dataPosition();
+            dest.writeInt(mPenaltyMask);
+            dest.writeInt(durationMillis);
+            dest.writeInt(violationNumThisLoop);
+            dest.writeInt(numAnimationsRunning);
+            dest.writeLong(violationUptimeMillis);
+            dest.writeLong(numInstances);
+            dest.writeString(broadcastIntentAction);
+            dest.writeStringArray(tags);
+            int total = dest.dataPosition() - start;
+            if (Binder.CHECK_PARCEL_SIZE && total > 10 * 1024) {
+                Slog.d(
+                        TAG,
+                        "VIO: penalty="
+                                + mPenaltyMask
+                                + " dur="
+                                + durationMillis
+                                + " numLoop="
+                                + violationNumThisLoop
+                                + " anim="
+                                + numAnimationsRunning
+                                + " uptime="
+                                + violationUptimeMillis
+                                + " numInst="
+                                + numInstances);
+                Slog.d(TAG, "VIO: action=" + broadcastIntentAction);
+                Slog.d(TAG, "VIO: tags=" + Arrays.toString(tags));
+                Slog.d(TAG, "VIO: TOTAL BYTES WRITTEN: " + (dest.dataPosition() - start));
+            }
+        }
+
+        /** Dump a ViolationInfo instance to a Printer. */
+        public void dump(Printer pw, String prefix) {
+            pw.println(prefix + "stackTrace: " + getStackTrace());
+            pw.println(prefix + "penalty: " + mPenaltyMask);
+            if (durationMillis != -1) {
+                pw.println(prefix + "durationMillis: " + durationMillis);
+            }
+            if (numInstances != -1) {
+                pw.println(prefix + "numInstances: " + numInstances);
+            }
+            if (violationNumThisLoop != 0) {
+                pw.println(prefix + "violationNumThisLoop: " + violationNumThisLoop);
+            }
+            if (numAnimationsRunning != 0) {
+                pw.println(prefix + "numAnimationsRunning: " + numAnimationsRunning);
+            }
+            pw.println(prefix + "violationUptimeMillis: " + violationUptimeMillis);
+            if (broadcastIntentAction != null) {
+                pw.println(prefix + "broadcastIntentAction: " + broadcastIntentAction);
+            }
+            if (tags != null) {
+                int index = 0;
+                for (String tag : tags) {
+                    pw.println(prefix + "tag[" + (index++) + "]: " + tag);
+                }
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final @android.annotation.NonNull Parcelable.Creator<ViolationInfo> CREATOR =
+                new Parcelable.Creator<ViolationInfo>() {
+                    @Override
+                    public ViolationInfo createFromParcel(Parcel in) {
+                        return new ViolationInfo(in);
+                    }
+
+                    @Override
+                    public ViolationInfo[] newArray(int size) {
+                        return new ViolationInfo[size];
+                    }
+                };
+    }
+
+    private static final class InstanceTracker {
+        private static final HashMap<Class<?>, Integer> sInstanceCounts =
+                new HashMap<Class<?>, Integer>();
+
+        private final Class<?> mKlass;
+
+        public InstanceTracker(Object instance) {
+            mKlass = instance.getClass();
+
+            synchronized (sInstanceCounts) {
+                final Integer value = sInstanceCounts.get(mKlass);
+                final int newValue = value != null ? value + 1 : 1;
+                sInstanceCounts.put(mKlass, newValue);
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                synchronized (sInstanceCounts) {
+                    final Integer value = sInstanceCounts.get(mKlass);
+                    if (value != null) {
+                        final int newValue = value - 1;
+                        if (newValue > 0) {
+                            sInstanceCounts.put(mKlass, newValue);
+                        } else {
+                            sInstanceCounts.remove(mKlass);
+                        }
+                    }
+                }
+            } finally {
+                super.finalize();
+            }
+        }
+
+        public static int getInstanceCount(Class<?> klass) {
+            synchronized (sInstanceCounts) {
+                final Integer value = sInstanceCounts.get(klass);
+                return value != null ? value : 0;
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/StrictModeTest.java b/android-34/android/os/StrictModeTest.java
new file mode 100644
index 0000000..60678e9
--- /dev/null
+++ b/android-34/android/os/StrictModeTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.os;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StrictModeTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeVmViolation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
+        causeVmViolations(state);
+    }
+
+    @Test
+    public void timeVmViolationNoStrictMode() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        causeVmViolations(state);
+    }
+
+    private static void causeVmViolations(BenchmarkState state) {
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setDataAndType(Uri.parse("content://com.example/foobar"), "image/jpeg");
+        final Context context = InstrumentationRegistry.getTargetContext();
+        while (state.keepRunning()) {
+            context.startActivity(intent);
+        }
+    }
+
+    @Test
+    public void timeThreadViolation() throws IOException {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        StrictMode.setThreadPolicy(
+                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+        causeThreadViolations(state);
+    }
+
+    @Test
+    public void timeThreadViolationNoStrictMode() throws IOException {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        causeThreadViolations(state);
+    }
+
+    private static void causeThreadViolations(BenchmarkState state) throws IOException {
+        final File test = File.createTempFile("foo", "bar");
+        while (state.keepRunning()) {
+            test.exists();
+        }
+        test.delete();
+    }
+
+    @Test
+    public void timeCrossBinderThreadViolation() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        StrictMode.setThreadPolicy(
+                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+        causeCrossProcessThreadViolations(state);
+    }
+
+    @Test
+    public void timeCrossBinderThreadViolationNoStrictMode() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        causeCrossProcessThreadViolations(state);
+    }
+
+    private static void causeCrossProcessThreadViolations(BenchmarkState state)
+            throws ExecutionException, InterruptedException, RemoteException {
+        final Context context = InstrumentationRegistry.getTargetContext();
+
+        SettableFuture<IBinder> binder = SettableFuture.create();
+        ServiceConnection connection =
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName className, IBinder service) {
+                        binder.set(service);
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName arg0) {
+                        binder.set(null);
+                    }
+                };
+        context.bindService(
+                new Intent(context, SomeService.class), connection, Context.BIND_AUTO_CREATE);
+        ISomeService someService = ISomeService.Stub.asInterface(binder.get());
+        while (state.keepRunning()) {
+            // Violate strictmode heavily.
+            someService.readDisk(10);
+        }
+        context.unbindService(connection);
+    }
+}
diff --git a/android-34/android/os/SynchronousResultReceiver.java b/android-34/android/os/SynchronousResultReceiver.java
new file mode 100644
index 0000000..6e1d4b3
--- /dev/null
+++ b/android-34/android/os/SynchronousResultReceiver.java
@@ -0,0 +1,94 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Extends ResultReceiver to allow the server end of the ResultReceiver to synchronously wait
+ * on the response from the client. This enables an RPC like system but with the ability to
+ * timeout and discard late results.
+ *
+ * NOTE: Can only be used for one response. Subsequent responses on the same instance are ignored.
+ * {@hide}
+ */
+public class SynchronousResultReceiver extends ResultReceiver {
+    public static class Result {
+        public int resultCode;
+        @Nullable public Bundle bundle;
+
+        public Result(int resultCode, @Nullable Bundle bundle) {
+            this.resultCode = resultCode;
+            this.bundle = bundle;
+        }
+    }
+
+    private final CompletableFuture<Result> mFuture = new CompletableFuture<>();
+    private final String mName;
+
+    public SynchronousResultReceiver() {
+        super((Handler) null);
+        mName = null;
+    }
+
+    /**
+     * @param name Name for logging purposes
+     */
+    public SynchronousResultReceiver(String name) {
+        super((Handler) null);
+        mName = name;
+    }
+
+    @Override
+    final protected void onReceiveResult(int resultCode, Bundle resultData) {
+        super.onReceiveResult(resultCode, resultData);
+        mFuture.complete(new Result(resultCode, resultData));
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Blocks waiting for the result from the remote client.
+     *
+     * @return the Result
+     * @throws TimeoutException if the timeout in milliseconds expired.
+     */
+    public @NonNull Result awaitResult(long timeoutMillis) throws TimeoutException {
+        final long deadline = System.currentTimeMillis() + timeoutMillis;
+        while (timeoutMillis >= 0) {
+            try {
+                return mFuture.get(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (ExecutionException e) {
+                // This will NEVER happen.
+                throw new AssertionError("Error receiving response", e);
+            } catch (InterruptedException e) {
+                // The thread was interrupted, try and get the value again, this time
+                // with the remaining time until the deadline.
+                timeoutMillis -= deadline - System.currentTimeMillis();
+            }
+        }
+        throw new TimeoutException();
+    }
+
+}
diff --git a/android-34/android/os/SystemClock.java b/android-34/android/os/SystemClock.java
new file mode 100644
index 0000000..831ca86
--- /dev/null
+++ b/android-34/android/os/SystemClock.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.app.IAlarmManager;
+import android.app.time.UnixEpochTime;
+import android.app.timedetector.ITimeDetectorService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.location.ILocationManager;
+import android.location.LocationTime;
+import android.util.Slog;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.ZoneOffset;
+
+/**
+ * Core timekeeping facilities.
+ *
+ * <p> Three different clocks are available, and they should not be confused:
+ *
+ * <ul>
+ *     <li> <p> {@link System#currentTimeMillis System.currentTimeMillis()}
+ *     is the standard "wall" clock (time and date) expressing milliseconds
+ *     since the epoch.  The wall clock can be set by the user or the phone
+ *     network (see {@link #setCurrentTimeMillis}), so the time may jump
+ *     backwards or forwards unpredictably.  This clock should only be used
+ *     when correspondence with real-world dates and times is important, such
+ *     as in a calendar or alarm clock application.  Interval or elapsed
+ *     time measurements should use a different clock.  If you are using
+ *     System.currentTimeMillis(), consider listening to the
+ *     {@link android.content.Intent#ACTION_TIME_TICK ACTION_TIME_TICK},
+ *     {@link android.content.Intent#ACTION_TIME_CHANGED ACTION_TIME_CHANGED}
+ *     and {@link android.content.Intent#ACTION_TIMEZONE_CHANGED
+ *     ACTION_TIMEZONE_CHANGED} {@link android.content.Intent Intent}
+ *     broadcasts to find out when the time changes.
+ *
+ *     <li> <p> {@link #uptimeMillis} is counted in milliseconds since the
+ *     system was booted.  This clock stops when the system enters deep
+ *     sleep (CPU off, display dark, device waiting for external input),
+ *     but is not affected by clock scaling, idle, or other power saving
+ *     mechanisms.  This is the basis for most interval timing
+ *     such as {@link Thread#sleep(long) Thread.sleep(millls)},
+ *     {@link Object#wait(long) Object.wait(millis)}, and
+ *     {@link System#nanoTime System.nanoTime()}.  This clock is guaranteed
+ *     to be monotonic, and is suitable for interval timing when the
+ *     interval does not span device sleep.  Most methods that accept a
+ *     timestamp value currently expect the {@link #uptimeMillis} clock.
+ *
+ *     <li> <p> {@link #elapsedRealtime} and {@link #elapsedRealtimeNanos}
+ *     return the time since the system was booted, and include deep sleep.
+ *     This clock is guaranteed to be monotonic, and continues to tick even
+ *     when the CPU is in power saving modes, so is the recommend basis
+ *     for general purpose interval timing.
+ *
+ * </ul>
+ *
+ * There are several mechanisms for controlling the timing of events:
+ *
+ * <ul>
+ *     <li> <p> Standard functions like {@link Thread#sleep(long)
+ *     Thread.sleep(millis)} and {@link Object#wait(long) Object.wait(millis)}
+ *     are always available.  These functions use the {@link #uptimeMillis}
+ *     clock; if the device enters sleep, the remainder of the time will be
+ *     postponed until the device wakes up.  These synchronous functions may
+ *     be interrupted with {@link Thread#interrupt Thread.interrupt()}, and
+ *     you must handle {@link InterruptedException}.
+ *
+ *     <li> <p> {@link #sleep SystemClock.sleep(millis)} is a utility function
+ *     very similar to {@link Thread#sleep(long) Thread.sleep(millis)}, but it
+ *     ignores {@link InterruptedException}.  Use this function for delays if
+ *     you do not use {@link Thread#interrupt Thread.interrupt()}, as it will
+ *     preserve the interrupted state of the thread.
+ *
+ *     <li> <p> The {@link android.os.Handler} class can schedule asynchronous
+ *     callbacks at an absolute or relative time.  Handler objects also use the
+ *     {@link #uptimeMillis} clock, and require an {@link android.os.Looper
+ *     event loop} (normally present in any GUI application).
+ *
+ *     <li> <p> The {@link android.app.AlarmManager} can trigger one-time or
+ *     recurring events which occur even when the device is in deep sleep
+ *     or your application is not running.  Events may be scheduled with your
+ *     choice of {@link java.lang.System#currentTimeMillis} (RTC) or
+ *     {@link #elapsedRealtime} (ELAPSED_REALTIME), and cause an
+ *     {@link android.content.Intent} broadcast when they occur.
+ * </ul>
+ */
+public final class SystemClock {
+    private static final String TAG = "SystemClock";
+
+    private static volatile IAlarmManager sIAlarmManager;
+
+    /**
+     * This class is uninstantiable.
+     */
+    @UnsupportedAppUsage
+    private SystemClock() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Waits a given number of milliseconds (of uptimeMillis) before returning.
+     * Similar to {@link java.lang.Thread#sleep(long)}, but does not throw
+     * {@link InterruptedException}; {@link Thread#interrupt()} events are
+     * deferred until the next interruptible operation.  Does not return until
+     * at least the specified number of milliseconds has elapsed.
+     *
+     * @param ms to sleep before returning, in milliseconds of uptime.
+     */
+    public static void sleep(long ms)
+    {
+        long start = uptimeMillis();
+        long duration = ms;
+        boolean interrupted = false;
+        do {
+            try {
+                Thread.sleep(duration);
+            }
+            catch (InterruptedException e) {
+                interrupted = true;
+            }
+            duration = start + ms - uptimeMillis();
+        } while (duration > 0);
+
+        if (interrupted) {
+            // Important: we don't want to quietly eat an interrupt() event,
+            // so we make sure to re-interrupt the thread so that the next
+            // call to Thread.sleep() or Object.wait() will be interrupted.
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    /**
+     * Sets the current wall time, in milliseconds.  Requires the calling
+     * process to have appropriate permissions.
+     *
+     * @return if the clock was successfully set to the specified time.
+     */
+    public static boolean setCurrentTimeMillis(long millis) {
+        final IAlarmManager mgr = getIAlarmManager();
+        if (mgr == null) {
+            Slog.e(TAG, "Unable to set RTC: mgr == null");
+            return false;
+        }
+
+        try {
+            return mgr.setTime(millis);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to set RTC", e);
+        } catch (SecurityException e) {
+            Slog.e(TAG, "Unable to set RTC", e);
+        }
+
+        return false;
+    }
+
+    private static IAlarmManager getIAlarmManager() {
+        if (sIAlarmManager == null) {
+            sIAlarmManager = IAlarmManager.Stub
+                    .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+        }
+        return sIAlarmManager;
+    }
+
+    /**
+     * Returns milliseconds since boot, not counting time spent in deep sleep.
+     *
+     * @return milliseconds of non-sleep uptime since boot.
+     */
+    @CriticalNative
+    native public static long uptimeMillis();
+
+    /**
+     * Returns nanoseconds since boot, not counting time spent in deep sleep.
+     *
+     * @return nanoseconds of non-sleep uptime since boot.
+     * @hide
+     */
+    @CriticalNative
+    public static native long uptimeNanos();
+
+    /**
+     * Return {@link Clock} that starts at system boot, not counting time spent
+     * in deep sleep.
+     *
+     * @removed
+     */
+    public static @NonNull Clock uptimeClock() {
+        return new SimpleClock(ZoneOffset.UTC) {
+            @Override
+            public long millis() {
+                return SystemClock.uptimeMillis();
+            }
+        };
+    }
+
+    /**
+     * Returns milliseconds since boot, including time spent in sleep.
+     *
+     * @return elapsed milliseconds since boot.
+     */
+    @CriticalNative
+    native public static long elapsedRealtime();
+
+    /**
+     * Return {@link Clock} that starts at system boot, including time spent in
+     * sleep.
+     *
+     * @removed
+     */
+    public static @NonNull Clock elapsedRealtimeClock() {
+        return new SimpleClock(ZoneOffset.UTC) {
+            @Override
+            public long millis() {
+                return SystemClock.elapsedRealtime();
+            }
+        };
+    }
+
+    /**
+     * Returns nanoseconds since boot, including time spent in sleep.
+     *
+     * @return elapsed nanoseconds since boot.
+     */
+    @CriticalNative
+    public static native long elapsedRealtimeNanos();
+
+    /**
+     * Returns milliseconds running in the current thread.
+     *
+     * @return elapsed milliseconds in the thread
+     */
+    @CriticalNative
+    public static native long currentThreadTimeMillis();
+
+    /**
+     * Returns microseconds running in the current thread.
+     *
+     * @return elapsed microseconds in the thread
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @CriticalNative
+    public static native long currentThreadTimeMicro();
+
+    /**
+     * Returns current wall time in  microseconds.
+     *
+     * @return elapsed microseconds in wall time
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @CriticalNative
+    public static native long currentTimeMicro();
+
+    /**
+     * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
+     * using a remote network source outside the device.
+     * <p>
+     * While the time returned by {@link System#currentTimeMillis()} can be
+     * adjusted by the user, the time returned by this method cannot be adjusted
+     * by the user.
+     * <p>
+     * This performs no blocking network operations and returns values based on
+     * a recent successful synchronization event; it will either return a valid
+     * time or throw.
+     * <p>
+     * Note that synchronization may occur using an insecure network protocol,
+     * so the returned time should not be used for security purposes.
+     * The device may resynchronize with the same or different network source
+     * at any time. Due to network delays, variations between servers, or local
+     * (client side) clock drift, the accuracy of the returned times cannot be
+     * guaranteed. In extreme cases, consecutive calls to {@link
+     * #currentNetworkTimeMillis()} could return times that are out of order.
+     *
+     * @throws DateTimeException when no network time can be provided.
+     * @hide
+     */
+    public static long currentNetworkTimeMillis() {
+        ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub
+                .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+        if (timeDetectorService != null) {
+            UnixEpochTime time;
+            try {
+                time = timeDetectorService.latestNetworkTime();
+            } catch (ParcelableException e) {
+                e.maybeRethrow(DateTimeException.class);
+                throw new RuntimeException(e);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+
+            if (time == null) {
+                // This is not expected.
+                throw new DateTimeException("Network based time is not available.");
+            }
+            long currentMillis = elapsedRealtime();
+            long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
+            return time.getUnixEpochTimeMillis() + deltaMs;
+        } else {
+            throw new RuntimeException(new DeadSystemException());
+        }
+    }
+
+   /**
+     * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC,
+     * synchronized using a remote network source outside the device.
+     * <p>
+     * While the time returned by {@link System#currentTimeMillis()} can be
+     * adjusted by the user, the time returned by this method cannot be adjusted
+     * by the user.
+     * <p>
+     * This performs no blocking network operations and returns values based on
+     * a recent successful synchronization event; it will either return a valid
+     * time or throw.
+     * <p>
+     * Note that synchronization may occur using an insecure network protocol,
+     * so the returned time should not be used for security purposes.
+     * The device may resynchronize with the same or different network source
+     * at any time. Due to network delays, variations between servers, or local
+     * (client side) clock drift, the accuracy of the returned times cannot be
+     * guaranteed. In extreme cases, consecutive calls to {@link
+     * Clock#millis()} on the returned {@link Clock}could return times that are
+     * out of order.
+     *
+     * @throws DateTimeException when no network time can be provided.
+     */
+    public static @NonNull Clock currentNetworkTimeClock() {
+        return new SimpleClock(ZoneOffset.UTC) {
+            @Override
+            public long millis() {
+                return SystemClock.currentNetworkTimeMillis();
+            }
+        };
+    }
+
+    /**
+     * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC,
+     * synchronized using the device's location provider.
+     *
+     * @throws DateTimeException when the location provider has not had a location fix since boot.
+     */
+    public static @NonNull Clock currentGnssTimeClock() {
+        return new SimpleClock(ZoneOffset.UTC) {
+            private final ILocationManager mMgr = ILocationManager.Stub
+                    .asInterface(ServiceManager.getService(Context.LOCATION_SERVICE));
+            @Override
+            public long millis() {
+                LocationTime time;
+                try {
+                    time = mMgr.getGnssTimeMillis();
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+                if (time == null) {
+                    throw new DateTimeException("Gnss based time is not available.");
+                }
+                long currentNanos = elapsedRealtimeNanos();
+                long deltaMs = (currentNanos - time.getElapsedRealtimeNanos()) / 1000000L;
+                return time.getUnixEpochTimeMillis() + deltaMs;
+            }
+        };
+    }
+}
diff --git a/android-34/android/os/SystemConfigManager.java b/android-34/android/os/SystemConfigManager.java
new file mode 100644
index 0000000..77843d9
--- /dev/null
+++ b/android-34/android/os/SystemConfigManager.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Allows apps outside the system process to access various bits of configuration defined in
+ * /etc/sysconfig and its counterparts on OEM and vendor partitions.
+ *
+ * TODO: Intended for access by system mainline modules only. Marking as SystemApi until the
+ * module-only API surface is available.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.SYSTEM_CONFIG_SERVICE)
+public class SystemConfigManager {
+    private static final String TAG = SystemConfigManager.class.getSimpleName();
+
+    private final ISystemConfig mInterface;
+
+    /** @hide **/
+    public SystemConfigManager() {
+        mInterface = ISystemConfig.Stub.asInterface(
+                ServiceManager.getService(Context.SYSTEM_CONFIG_SERVICE));
+    }
+
+    /**
+     * Returns a set of package names for carrier apps that are preinstalled on the device but
+     * should be disabled until the matching carrier's SIM is inserted into the device.
+     * @return A set of package names.
+     */
+    @RequiresPermission(Manifest.permission.READ_CARRIER_APP_INFO)
+    public @NonNull Set<String> getDisabledUntilUsedPreinstalledCarrierApps() {
+        try {
+            List<String> apps = mInterface.getDisabledUntilUsedPreinstalledCarrierApps();
+            return new ArraySet<>(apps);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Caught remote exception");
+            return Collections.emptySet();
+        }
+    }
+
+    /**
+     * Returns a map that describes helper apps associated with carrier apps that, like the apps
+     * returned by {@link #getDisabledUntilUsedPreinstalledCarrierApps()}, should be disabled until
+     * the correct SIM is inserted into the device.
+     * @return A map with keys corresponding to package names returned by
+     *         {@link #getDisabledUntilUsedPreinstalledCarrierApps()} and values as lists of package
+     *         names of helper apps.
+     */
+    @RequiresPermission(Manifest.permission.READ_CARRIER_APP_INFO)
+    public @NonNull Map<String, List<String>>
+            getDisabledUntilUsedPreinstalledCarrierAssociatedApps() {
+        try {
+            return (Map<String, List<String>>)
+                    mInterface.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Caught remote exception");
+            return Collections.emptyMap();
+        }
+    }
+
+    /**
+     * Returns a map that describes helper apps associated with carrier apps that, like the apps
+     * returned by {@link #getDisabledUntilUsedPreinstalledCarrierApps()}, should be disabled until
+     * the correct SIM is inserted into the device.
+     *
+     * <p>TODO(b/159069037) expose this and get rid of the other method that omits SDK version.
+     *
+     * @return A map with keys corresponding to package names returned by
+     *         {@link #getDisabledUntilUsedPreinstalledCarrierApps()} and values as lists of package
+     *         names of helper apps and the SDK versions when they were first added.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_CARRIER_APP_INFO)
+    public @NonNull Map<String, List<CarrierAssociatedAppEntry>>
+            getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries() {
+        try {
+            return (Map<String, List<CarrierAssociatedAppEntry>>)
+                    mInterface.getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Caught remote exception", e);
+            return Collections.emptyMap();
+        }
+    }
+
+    /**
+     * Get uids which have been granted given permission in system configuration.
+     *
+     * The uids and assigning permissions are defined on data/etc/platform.xml
+     *
+     * @param permissionName The target permission.
+     * @return The uids have been granted given permission in system configuration.
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    @NonNull
+    public int[] getSystemPermissionUids(@NonNull String permissionName) {
+        try {
+            return mInterface.getSystemPermissionUids(permissionName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get enabled component for a specific package
+     *
+     * @param packageName The target package.
+     * @return The enabled component
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
+    public List<ComponentName> getEnabledComponentOverrides(@NonNull String packageName) {
+        try {
+            return mInterface.getEnabledComponentOverrides(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the components that are enabled by default as VR mode listener services.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)
+    public List<ComponentName> getDefaultVrComponents() {
+        try {
+            return mInterface.getDefaultVrComponents();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return Collections.emptyList();
+    }
+}
diff --git a/android-34/android/os/SystemProperties.java b/android-34/android/os/SystemProperties.java
new file mode 100644
index 0000000..aa283a2
--- /dev/null
+++ b/android-34/android/os/SystemProperties.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
+import android.util.MutableInt;
+
+import com.android.internal.annotations.GuardedBy;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.HexEncoding;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * Gives access to the system properties store.  The system properties
+ * store contains a list of string key-value pairs.
+ *
+ * <p>Use this class only for the system properties that are local. e.g., within
+ * an app, a partition, or a module. For system properties used across the
+ * boundaries, formally define them in <code>*.sysprop</code> files and use the
+ * auto-generated methods. For more information, see <a href=
+ * "https://source.android.com/devices/architecture/sysprops-apis">Implementing
+ * System Properties as APIs</a>.</p>
+ *
+ * {@hide}
+ */
+@SystemApi
+public class SystemProperties {
+    private static final String TAG = "SystemProperties";
+    private static final boolean TRACK_KEY_ACCESS = false;
+
+    /**
+     * Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5
+     * uses reflection to read this whenever text is selected (http://b/36095274).
+     * @hide
+     */
+    @UnsupportedAppUsage(trackingBug = 172649311)
+    public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
+
+    /** @hide */
+    public static final int PROP_VALUE_MAX = 91;
+
+    @UnsupportedAppUsage
+    @GuardedBy("sChangeCallbacks")
+    private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
+
+    @GuardedBy("sRoReads")
+    private static final HashMap<String, MutableInt> sRoReads =
+            TRACK_KEY_ACCESS ? new HashMap<>() : null;
+
+    private static void onKeyAccess(String key) {
+        if (!TRACK_KEY_ACCESS) return;
+
+        if (key != null && key.startsWith("ro.")) {
+            synchronized (sRoReads) {
+                MutableInt numReads = sRoReads.getOrDefault(key, null);
+                if (numReads == null) {
+                    numReads = new MutableInt(0);
+                    sRoReads.put(key, numReads);
+                }
+                numReads.value++;
+                if (numReads.value > 3) {
+                    Log.d(TAG, "Repeated read (count=" + numReads.value
+                            + ") of a read-only system property '" + key + "'",
+                            new Exception());
+                }
+            }
+        }
+    }
+
+    // The one-argument version of native_get used to be a regular native function. Nowadays,
+    // we use the two-argument form of native_get all the time, but we can't just delete the
+    // one-argument overload: apps use it via reflection, as the UnsupportedAppUsage annotation
+    // indicates. Let's just live with having a Java function with a very unusual name.
+    @UnsupportedAppUsage
+    private static String native_get(String key) {
+        return native_get(key, "");
+    }
+
+    @FastNative
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private static native String native_get(String key, String def);
+    @FastNative
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private static native int native_get_int(String key, int def);
+    @FastNative
+    @UnsupportedAppUsage
+    private static native long native_get_long(String key, long def);
+    @FastNative
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private static native boolean native_get_boolean(String key, boolean def);
+
+    @FastNative
+    private static native long native_find(String name);
+    @FastNative
+    private static native String native_get(long handle);
+    @CriticalNative
+    private static native int native_get_int(long handle, int def);
+    @CriticalNative
+    private static native long native_get_long(long handle, long def);
+    @CriticalNative
+    private static native boolean native_get_boolean(long handle, boolean def);
+
+    // _NOT_ FastNative: native_set performs IPC and can block
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private static native void native_set(String key, String def);
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private static native void native_add_change_callback();
+    private static native void native_report_sysprop_change();
+
+    /**
+     * Get the String value for the given {@code key}.
+     *
+     * @param key the key to lookup
+     * @return an empty string if the {@code key} isn't found
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static String get(@NonNull String key) {
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
+        return native_get(key);
+    }
+
+    /**
+     * Get the String value for the given {@code key}.
+     *
+     * @param key the key to lookup
+     * @param def the default value in case the property is not set or empty
+     * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
+     * string otherwise
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static String get(@NonNull String key, @Nullable String def) {
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
+        return native_get(key, def);
+    }
+
+    /**
+     * Get the value for the given {@code key}, and return as an integer.
+     *
+     * @param key the key to lookup
+     * @param def a default value to return
+     * @return the key parsed as an integer, or def if the key isn't found or
+     *         cannot be parsed
+     * @hide
+     */
+    @SystemApi
+    public static int getInt(@NonNull String key, int def) {
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
+        return native_get_int(key, def);
+    }
+
+    /**
+     * Get the value for the given {@code key}, and return as a long.
+     *
+     * @param key the key to lookup
+     * @param def a default value to return
+     * @return the key parsed as a long, or def if the key isn't found or
+     *         cannot be parsed
+     * @hide
+     */
+    @SystemApi
+    public static long getLong(@NonNull String key, long def) {
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
+        return native_get_long(key, def);
+    }
+
+    /**
+     * Get the value for the given {@code key}, returned as a boolean.
+     * Values 'n', 'no', '0', 'false' or 'off' are considered false.
+     * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
+     * (case sensitive).
+     * If the key does not exist, or has any other value, then the default
+     * result is returned.
+     *
+     * @param key the key to lookup
+     * @param def a default value to return
+     * @return the key parsed as a boolean, or def if the key isn't found or is
+     *         not able to be parsed as a boolean.
+     * @hide
+     */
+    @SystemApi
+    public static boolean getBoolean(@NonNull String key, boolean def) {
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
+        return native_get_boolean(key, def);
+    }
+
+    /**
+     * Set the value for the given {@code key} to {@code val}.
+     *
+     * @throws IllegalArgumentException for non read-only properties if the {@code val} exceeds
+     * 91 characters
+     * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+     * SELinux. libc will log the underlying reason.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void set(@NonNull String key, @Nullable String val) {
+        if (val != null && !key.startsWith("ro.") && val.getBytes(StandardCharsets.UTF_8).length
+                > PROP_VALUE_MAX) {
+            throw new IllegalArgumentException("value of system property '" + key
+                    + "' is longer than " + PROP_VALUE_MAX + " bytes: " + val);
+        }
+        if (TRACK_KEY_ACCESS) onKeyAccess(key);
+        native_set(key, val);
+    }
+
+    /**
+     * Add a callback that will be run whenever any system property changes.
+     *
+     * @param callback The {@link Runnable} that should be executed when a system property
+     * changes.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void addChangeCallback(@NonNull Runnable callback) {
+        synchronized (sChangeCallbacks) {
+            if (sChangeCallbacks.size() == 0) {
+                native_add_change_callback();
+            }
+            sChangeCallbacks.add(callback);
+        }
+    }
+
+    /**
+     * Remove the target callback.
+     *
+     * @param callback The {@link Runnable} that should be removed.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static void removeChangeCallback(@NonNull Runnable callback) {
+        synchronized (sChangeCallbacks) {
+            if (sChangeCallbacks.contains(callback)) {
+                sChangeCallbacks.remove(callback);
+            }
+        }
+    }
+
+    @SuppressWarnings("unused")  // Called from native code.
+    private static void callChangeCallbacks() {
+        ArrayList<Runnable> callbacks = null;
+        synchronized (sChangeCallbacks) {
+            //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
+            if (sChangeCallbacks.size() == 0) {
+                return;
+            }
+            callbacks = new ArrayList<Runnable>(sChangeCallbacks);
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            for (int i = 0; i < callbacks.size(); i++) {
+                try {
+                    callbacks.get(i).run();
+                } catch (Throwable t) {
+                    // Ignore and try to go on. Don't use wtf here: that
+                    // will cause the process to exit on some builds and break tests.
+                    Log.e(TAG, "Exception in SystemProperties change callback", t);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Notifies listeners that a system property has changed
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void reportSyspropChanged() {
+        native_report_sysprop_change();
+    }
+
+    /**
+     * Return a {@code SHA-1} digest of the given keys and their values as a
+     * hex-encoded string. The ordering of the incoming keys doesn't change the
+     * digest result.
+     *
+     * @hide
+     */
+    public static @NonNull String digestOf(@NonNull String... keys) {
+        Arrays.sort(keys);
+        try {
+            final MessageDigest digest = MessageDigest.getInstance("SHA-1");
+            for (String key : keys) {
+                final String item = key + "=" + get(key) + "\n";
+                digest.update(item.getBytes(StandardCharsets.UTF_8));
+            }
+            return HexEncoding.encodeToString(digest.digest()).toLowerCase();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @UnsupportedAppUsage
+    private SystemProperties() {
+    }
+
+    /**
+     * Look up a property location by name.
+     * @name name of the property
+     * @return property handle or {@code null} if property isn't set
+     * @hide
+     */
+    @Nullable public static Handle find(@NonNull String name) {
+        long nativeHandle = native_find(name);
+        if (nativeHandle == 0) {
+            return null;
+        }
+        return new Handle(nativeHandle);
+    }
+
+    /**
+     * Handle to a pre-located property. Looking up a property handle in advance allows
+     * for optimal repeated lookup of a single property.
+     * @hide
+     */
+    public static final class Handle {
+
+        private final long mNativeHandle;
+
+        /**
+         * @return Value of the property
+         */
+        @NonNull public String get() {
+            return native_get(mNativeHandle);
+        }
+        /**
+         * @param def default value
+         * @return value or {@code def} on parse error
+         */
+        public int getInt(int def) {
+            return native_get_int(mNativeHandle, def);
+        }
+        /**
+         * @param def default value
+         * @return value or {@code def} on parse error
+         */
+        public long getLong(long def) {
+            return native_get_long(mNativeHandle, def);
+        }
+        /**
+         * @param def default value
+         * @return value or {@code def} on parse error
+         */
+        public boolean getBoolean(boolean def) {
+            return native_get_boolean(mNativeHandle, def);
+        }
+
+        private Handle(long nativeHandle) {
+            mNativeHandle = nativeHandle;
+        }
+    }
+}
diff --git a/android-34/android/os/SystemService.java b/android-34/android/os/SystemService.java
new file mode 100644
index 0000000..9b0ac8f
--- /dev/null
+++ b/android-34/android/os/SystemService.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import com.google.android.collect.Maps;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Controls and utilities for low-level {@code init} services.
+ *
+ * @hide
+ */
+public class SystemService {
+
+    private static HashMap<String, State> sStates = Maps.newHashMap();
+
+    /**
+     * State of a known {@code init} service.
+     */
+    public enum State {
+        RUNNING("running"),
+        STOPPING("stopping"),
+        STOPPED("stopped"),
+        RESTARTING("restarting");
+
+        State(String state) {
+            sStates.put(state, this);
+        }
+    }
+
+    private static Object sPropertyLock = new Object();
+
+    static {
+        SystemProperties.addChangeCallback(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (sPropertyLock) {
+                    sPropertyLock.notifyAll();
+                }
+            }
+        });
+    }
+
+    /** Request that the init daemon start a named service. */
+    @UnsupportedAppUsage
+    public static void start(String name) {
+        SystemProperties.set("ctl.start", name);
+    }
+
+    /** Request that the init daemon stop a named service. */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static void stop(String name) {
+        SystemProperties.set("ctl.stop", name);
+    }
+
+    /** Request that the init daemon restart a named service. */
+    public static void restart(String name) {
+        SystemProperties.set("ctl.restart", name);
+    }
+
+    /**
+     * Return current state of given service.
+     */
+    public static State getState(String service) {
+        final String rawState = SystemProperties.get("init.svc." + service);
+        final State state = sStates.get(rawState);
+        if (state != null) {
+            return state;
+        } else {
+            return State.STOPPED;
+        }
+    }
+
+    /**
+     * Check if given service is {@link State#STOPPED}.
+     */
+    public static boolean isStopped(String service) {
+        return State.STOPPED.equals(getState(service));
+    }
+
+    /**
+     * Check if given service is {@link State#RUNNING}.
+     */
+    public static boolean isRunning(String service) {
+        return State.RUNNING.equals(getState(service));
+    }
+
+    /**
+     * Wait until given service has entered specific state.
+     */
+    public static void waitForState(String service, State state, long timeoutMillis)
+            throws TimeoutException {
+        final long endMillis = SystemClock.elapsedRealtime() + timeoutMillis;
+        while (true) {
+            synchronized (sPropertyLock) {
+                final State currentState = getState(service);
+                if (state.equals(currentState)) {
+                    return;
+                }
+
+                if (SystemClock.elapsedRealtime() >= endMillis) {
+                    throw new TimeoutException("Service " + service + " currently " + currentState
+                            + "; waited " + timeoutMillis + "ms for " + state);
+                }
+
+                try {
+                    sPropertyLock.wait(timeoutMillis);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Wait until any of given services enters {@link State#STOPPED}.
+     */
+    public static void waitForAnyStopped(String... services)  {
+        while (true) {
+            synchronized (sPropertyLock) {
+                for (String service : services) {
+                    if (State.STOPPED.equals(getState(service))) {
+                        return;
+                    }
+                }
+
+                try {
+                    sPropertyLock.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/SystemUpdateManager.java b/android-34/android/os/SystemUpdateManager.java
new file mode 100644
index 0000000..9146731
--- /dev/null
+++ b/android-34/android/os/SystemUpdateManager.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Allows querying and posting system update information.
+ *
+ * {@hide}
+ */
+@SystemApi
+@SystemService(Context.SYSTEM_UPDATE_SERVICE)
+public class SystemUpdateManager {
+    private static final String TAG = "SystemUpdateManager";
+
+    /** The status key of the system update info, expecting an int value. */
+    public static final String KEY_STATUS = "status";
+
+    /** The title of the current update, expecting a String value. */
+    public static final String KEY_TITLE = "title";
+
+    /** Whether it is a security update, expecting a boolean value. */
+    public static final String KEY_IS_SECURITY_UPDATE = "is_security_update";
+
+    /** The build fingerprint after installing the current update, expecting a String value. */
+    public static final String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
+
+    /** The security patch level after installing the current update, expecting a String value. */
+    public static final String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
+
+    /**
+     * The KEY_STATUS value that indicates there's no update status info available.
+     */
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * The KEY_STATUS value that indicates there's no pending update.
+     */
+    public static final int STATUS_IDLE = 1;
+
+    /**
+     * The KEY_STATUS value that indicates an update is available for download, but pending user
+     * approval to start.
+     */
+    public static final int STATUS_WAITING_DOWNLOAD = 2;
+
+    /**
+     * The KEY_STATUS value that indicates an update is in progress (i.e. downloading or installing
+     * has started).
+     */
+    public static final int STATUS_IN_PROGRESS = 3;
+
+    /**
+     * The KEY_STATUS value that indicates an update is available for install.
+     */
+    public static final int STATUS_WAITING_INSTALL = 4;
+
+    /**
+     * The KEY_STATUS value that indicates an update will be installed after a reboot. This applies
+     * to both of A/B and non-A/B OTAs.
+     */
+    public static final int STATUS_WAITING_REBOOT = 5;
+
+    private final ISystemUpdateManager mService;
+
+    /** @hide */
+    public SystemUpdateManager(ISystemUpdateManager service) {
+        mService = checkNotNull(service, "missing ISystemUpdateManager");
+    }
+
+    /**
+     * Queries the current pending system update info.
+     *
+     * <p>Requires the {@link android.Manifest.permission#READ_SYSTEM_UPDATE_INFO} or
+     * {@link android.Manifest.permission#RECOVERY} permission.
+     *
+     * @return A {@code Bundle} that contains the pending system update information in key-value
+     * pairs.
+     *
+     * @throws SecurityException if the caller is not allowed to read the info.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_SYSTEM_UPDATE_INFO,
+            android.Manifest.permission.RECOVERY,
+    })
+    public Bundle retrieveSystemUpdateInfo() {
+        try {
+            return mService.retrieveSystemUpdateInfo();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allows a system updater to publish the pending update info.
+     *
+     * <p>The reported info will not persist across reboots. Because only the reporting updater
+     * understands the criteria to determine a successful/failed update.
+     *
+     * <p>Requires the {@link android.Manifest.permission#RECOVERY} permission.
+     *
+     * @param infoBundle The {@code PersistableBundle} that contains the system update information,
+     * such as the current update status. {@link #KEY_STATUS} is required in the bundle.
+     *
+     * @throws IllegalArgumentException if @link #KEY_STATUS} does not exist.
+     * @throws SecurityException if the caller is not allowed to update the info.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
+        if (infoBundle == null || !infoBundle.containsKey(KEY_STATUS)) {
+            throw new IllegalArgumentException("Missing status in the bundle");
+        }
+        try {
+            mService.updateSystemUpdateInfo(infoBundle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-34/android/os/SystemVibrator.java b/android-34/android/os/SystemVibrator.java
new file mode 100644
index 0000000..bf72b1d
--- /dev/null
+++ b/android-34/android/os/SystemVibrator.java
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.hardware.vibrator.IVibrator;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Range;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Function;
+
+/**
+ * Vibrator implementation that controls the main system vibrator.
+ *
+ * @hide
+ */
+public class SystemVibrator extends Vibrator {
+    private static final String TAG = "Vibrator";
+
+    private final VibratorManager mVibratorManager;
+    private final Context mContext;
+
+    @GuardedBy("mBrokenListeners")
+    private final ArrayList<MultiVibratorStateListener> mBrokenListeners = new ArrayList<>();
+
+    @GuardedBy("mRegisteredListeners")
+    private final ArrayMap<OnVibratorStateChangedListener, MultiVibratorStateListener>
+            mRegisteredListeners = new ArrayMap<>();
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private VibratorInfo mVibratorInfo;
+
+    @UnsupportedAppUsage
+    public SystemVibrator(Context context) {
+        super(context);
+        mContext = context;
+        mVibratorManager = mContext.getSystemService(VibratorManager.class);
+    }
+
+    @Override
+    protected VibratorInfo getInfo() {
+        synchronized (mLock) {
+            if (mVibratorInfo != null) {
+                return mVibratorInfo;
+            }
+            if (mVibratorManager == null) {
+                Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager.");
+                return VibratorInfo.EMPTY_VIBRATOR_INFO;
+            }
+            int[] vibratorIds = mVibratorManager.getVibratorIds();
+            if (vibratorIds.length == 0) {
+                // It is known that the device has no vibrator, so cache and return info that
+                // reflects the lack of support for effects/primitives.
+                return mVibratorInfo = new NoVibratorInfo();
+            }
+            VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
+            for (int i = 0; i < vibratorIds.length; i++) {
+                Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]);
+                if (vibrator instanceof NullVibrator) {
+                    Log.w(TAG, "Vibrator manager service not ready; "
+                            + "Info not yet available for vibrator: " + vibratorIds[i]);
+                    // This should never happen after the vibrator manager service is ready.
+                    // Skip caching this vibrator until then.
+                    return VibratorInfo.EMPTY_VIBRATOR_INFO;
+                }
+                vibratorInfos[i] = vibrator.getInfo();
+            }
+            if (vibratorInfos.length == 1) {
+                // Device has a single vibrator info, cache and return successfully loaded info.
+                return mVibratorInfo = new VibratorInfo(/* id= */ -1, vibratorInfos[0]);
+            }
+            // Device has multiple vibrators, generate a single info representing all of them.
+            return mVibratorInfo = new MultiVibratorInfo(vibratorInfos);
+        }
+    }
+
+    @Override
+    public boolean hasVibrator() {
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to check if vibrator exists; no vibrator manager.");
+            return false;
+        }
+        return mVibratorManager.getVibratorIds().length > 0;
+    }
+
+    @Override
+    public boolean isVibrating() {
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator manager.");
+            return false;
+        }
+        for (int vibratorId : mVibratorManager.getVibratorIds()) {
+            if (mVibratorManager.getVibrator(vibratorId).isVibrating()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+        Objects.requireNonNull(listener);
+        if (mContext == null) {
+            Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
+            return;
+        }
+        addVibratorStateListener(mContext.getMainExecutor(), listener);
+    }
+
+    @Override
+    public void addVibratorStateListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnVibratorStateChangedListener listener) {
+        Objects.requireNonNull(listener);
+        Objects.requireNonNull(executor);
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to add vibrate state listener; no vibrator manager.");
+            return;
+        }
+        MultiVibratorStateListener delegate = null;
+        try {
+            synchronized (mRegisteredListeners) {
+                // If listener is already registered, reject and return.
+                if (mRegisteredListeners.containsKey(listener)) {
+                    Log.w(TAG, "Listener already registered.");
+                    return;
+                }
+                delegate = new MultiVibratorStateListener(executor, listener);
+                delegate.register(mVibratorManager);
+                mRegisteredListeners.put(listener, delegate);
+                delegate = null;
+            }
+        } finally {
+            if (delegate != null && delegate.hasRegisteredListeners()) {
+                // The delegate listener was left in a partial state with listeners registered to
+                // some but not all vibrators. Keep track of this to try to unregister them later.
+                synchronized (mBrokenListeners) {
+                    mBrokenListeners.add(delegate);
+                }
+            }
+            tryUnregisterBrokenListeners();
+        }
+    }
+
+    @Override
+    public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+        Objects.requireNonNull(listener);
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to remove vibrate state listener; no vibrator manager.");
+            return;
+        }
+        synchronized (mRegisteredListeners) {
+            if (mRegisteredListeners.containsKey(listener)) {
+                MultiVibratorStateListener delegate = mRegisteredListeners.get(listener);
+                delegate.unregister(mVibratorManager);
+                mRegisteredListeners.remove(listener);
+            }
+        }
+        tryUnregisterBrokenListeners();
+    }
+
+    @Override
+    public boolean hasAmplitudeControl() {
+        return getInfo().hasAmplitudeControl();
+    }
+
+    @Override
+    public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
+            VibrationAttributes attrs) {
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to set always-on effect; no vibrator manager.");
+            return false;
+        }
+        CombinedVibration combinedEffect = CombinedVibration.createParallel(effect);
+        return mVibratorManager.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combinedEffect, attrs);
+    }
+
+    @Override
+    public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect,
+            String reason, @NonNull VibrationAttributes attributes) {
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator manager.");
+            return;
+        }
+        CombinedVibration combinedEffect = CombinedVibration.createParallel(effect);
+        mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes);
+    }
+
+    @Override
+    public void cancel() {
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to cancel vibrate; no vibrator manager.");
+            return;
+        }
+        mVibratorManager.cancel();
+    }
+
+    @Override
+    public void cancel(int usageFilter) {
+        if (mVibratorManager == null) {
+            Log.w(TAG, "Failed to cancel vibrate; no vibrator manager.");
+            return;
+        }
+        mVibratorManager.cancel(usageFilter);
+    }
+
+    /**
+     * Tries to unregister individual {@link android.os.Vibrator.OnVibratorStateChangedListener}
+     * that were left registered to vibrators after failures to register them to all vibrators.
+     *
+     * <p>This might happen if {@link MultiVibratorStateListener} fails to register to any vibrator
+     * and also fails to unregister any previously registered single listeners to other vibrators.
+     *
+     * <p>This method never throws {@link RuntimeException} if it fails to unregister again, it will
+     * fail silently and attempt to unregister the same broken listener later.
+     */
+    private void tryUnregisterBrokenListeners() {
+        synchronized (mBrokenListeners) {
+            try {
+                for (int i = mBrokenListeners.size(); --i >= 0; ) {
+                    mBrokenListeners.get(i).unregister(mVibratorManager);
+                    mBrokenListeners.remove(i);
+                }
+            } catch (RuntimeException e) {
+                Log.w(TAG, "Failed to unregister broken listener", e);
+            }
+        }
+    }
+
+    /** Listener for a single vibrator state change. */
+    private static class SingleVibratorStateListener implements OnVibratorStateChangedListener {
+        private final MultiVibratorStateListener mAllVibratorsListener;
+        private final int mVibratorIdx;
+
+        SingleVibratorStateListener(MultiVibratorStateListener listener, int vibratorIdx) {
+            mAllVibratorsListener = listener;
+            mVibratorIdx = vibratorIdx;
+        }
+
+        @Override
+        public void onVibratorStateChanged(boolean isVibrating) {
+            mAllVibratorsListener.onVibrating(mVibratorIdx, isVibrating);
+        }
+    }
+
+    /**
+     * Represents a device with no vibrator as a single {@link VibratorInfo}.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static class NoVibratorInfo extends VibratorInfo {
+        public NoVibratorInfo() {
+            // Use empty arrays to indicate no support, while null would indicate support unknown.
+            super(/* id= */ -1,
+                    /* capabilities= */ 0,
+                    /* supportedEffects= */ new SparseBooleanArray(),
+                    /* supportedBraking= */ new SparseBooleanArray(),
+                    /* supportedPrimitives= */ new SparseIntArray(),
+                    /* primitiveDelayMax= */ 0,
+                    /* compositionSizeMax= */ 0,
+                    /* pwlePrimitiveDurationMax= */ 0,
+                    /* pwleSizeMax= */ 0,
+                    /* qFactor= */ Float.NaN,
+                    new FrequencyProfile(/* resonantFrequencyHz= */ Float.NaN,
+                            /* minFrequencyHz= */ Float.NaN,
+                            /* frequencyResolutionHz= */ Float.NaN,
+                            /* maxAmplitudes= */ null));
+        }
+    }
+
+    /**
+     * Represents multiple vibrator information as a single {@link VibratorInfo}.
+     *
+     * <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive
+     * support.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static class MultiVibratorInfo extends VibratorInfo {
+        // Epsilon used for float comparison applied in calculations for the merged info.
+        private static final float EPSILON = 1e-5f;
+
+        public MultiVibratorInfo(VibratorInfo[] vibrators) {
+            // Need to use an extra constructor to share the computation in super initialization.
+            this(vibrators, frequencyProfileIntersection(vibrators));
+        }
+
+        private MultiVibratorInfo(VibratorInfo[] vibrators,
+                VibratorInfo.FrequencyProfile mergedProfile) {
+            super(/* id= */ -1,
+                    capabilitiesIntersection(vibrators, mergedProfile.isEmpty()),
+                    supportedEffectsIntersection(vibrators),
+                    supportedBrakingIntersection(vibrators),
+                    supportedPrimitivesAndDurationsIntersection(vibrators),
+                    integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax),
+                    integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax),
+                    integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
+                    integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
+                    floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
+                    mergedProfile);
+        }
+
+        private static int capabilitiesIntersection(VibratorInfo[] infos,
+                boolean frequencyProfileIsEmpty) {
+            int intersection = ~0;
+            for (VibratorInfo info : infos) {
+                intersection &= info.getCapabilities();
+            }
+            if (frequencyProfileIsEmpty) {
+                // Revoke frequency control if the merged frequency profile ended up empty.
+                intersection &= ~IVibrator.CAP_FREQUENCY_CONTROL;
+            }
+            return intersection;
+        }
+
+        @Nullable
+        private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) {
+            for (VibratorInfo info : infos) {
+                if (!info.isBrakingSupportKnown()) {
+                    // If one vibrator support is unknown, then the intersection is also unknown.
+                    return null;
+                }
+            }
+
+            SparseBooleanArray intersection = new SparseBooleanArray();
+            SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking();
+
+            brakingIdLoop:
+            for (int i = 0; i < firstVibratorBraking.size(); i++) {
+                int brakingId = firstVibratorBraking.keyAt(i);
+                if (!firstVibratorBraking.valueAt(i)) {
+                    // The first vibrator already doesn't support this braking, so skip it.
+                    continue brakingIdLoop;
+                }
+
+                for (int j = 1; j < infos.length; j++) {
+                    if (!infos[j].hasBrakingSupport(brakingId)) {
+                        // One vibrator doesn't support this braking, so the intersection doesn't.
+                        continue brakingIdLoop;
+                    }
+                }
+
+                intersection.put(brakingId, true);
+            }
+
+            return intersection;
+        }
+
+        @Nullable
+        private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) {
+            for (VibratorInfo info : infos) {
+                if (!info.isEffectSupportKnown()) {
+                    // If one vibrator support is unknown, then the intersection is also unknown.
+                    return null;
+                }
+            }
+
+            SparseBooleanArray intersection = new SparseBooleanArray();
+            SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects();
+
+            effectIdLoop:
+            for (int i = 0; i < firstVibratorEffects.size(); i++) {
+                int effectId = firstVibratorEffects.keyAt(i);
+                if (!firstVibratorEffects.valueAt(i)) {
+                    // The first vibrator already doesn't support this effect, so skip it.
+                    continue effectIdLoop;
+                }
+
+                for (int j = 1; j < infos.length; j++) {
+                    if (infos[j].isEffectSupported(effectId) != VIBRATION_EFFECT_SUPPORT_YES) {
+                        // One vibrator doesn't support this effect, so the intersection doesn't.
+                        continue effectIdLoop;
+                    }
+                }
+
+                intersection.put(effectId, true);
+            }
+
+            return intersection;
+        }
+
+        @NonNull
+        private static SparseIntArray supportedPrimitivesAndDurationsIntersection(
+                VibratorInfo[] infos) {
+            SparseIntArray intersection = new SparseIntArray();
+            SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives();
+
+            primitiveIdLoop:
+            for (int i = 0; i < firstVibratorPrimitives.size(); i++) {
+                int primitiveId = firstVibratorPrimitives.keyAt(i);
+                int primitiveDuration = firstVibratorPrimitives.valueAt(i);
+                if (primitiveDuration == 0) {
+                    // The first vibrator already doesn't support this primitive, so skip it.
+                    continue primitiveIdLoop;
+                }
+
+                for (int j = 1; j < infos.length; j++) {
+                    int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId);
+                    if (vibratorPrimitiveDuration == 0) {
+                        // One vibrator doesn't support this primitive, so the intersection doesn't.
+                        continue primitiveIdLoop;
+                    } else {
+                        // The primitive vibration duration is the maximum among all vibrators.
+                        primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration);
+                    }
+                }
+
+                intersection.put(primitiveId, primitiveDuration);
+            }
+            return intersection;
+        }
+
+        private static int integerLimitIntersection(VibratorInfo[] infos,
+                Function<VibratorInfo, Integer> propertyGetter) {
+            int limit = 0; // Limit 0 means unlimited
+            for (VibratorInfo info : infos) {
+                int vibratorLimit = propertyGetter.apply(info);
+                if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) {
+                    // This vibrator is limited and intersection is unlimited or has a larger limit:
+                    // use smaller limit here for the intersection.
+                    limit = vibratorLimit;
+                }
+            }
+            return limit;
+        }
+
+        private static float floatPropertyIntersection(VibratorInfo[] infos,
+                Function<VibratorInfo, Float> propertyGetter) {
+            float property = propertyGetter.apply(infos[0]);
+            if (Float.isNaN(property)) {
+                // If one vibrator is undefined then the intersection is undefined.
+                return Float.NaN;
+            }
+            for (int i = 1; i < infos.length; i++) {
+                if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) {
+                    // If one vibrator has a different value then the intersection is undefined.
+                    return Float.NaN;
+                }
+            }
+            return property;
+        }
+
+        @NonNull
+        private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) {
+            float freqResolution = floatPropertyIntersection(infos,
+                    info -> info.getFrequencyProfile().getFrequencyResolutionHz());
+            float resonantFreq = floatPropertyIntersection(infos,
+                    VibratorInfo::getResonantFrequencyHz);
+            Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution);
+
+            if ((freqRange == null) || Float.isNaN(freqResolution)) {
+                return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null);
+            }
+
+            int amplitudeCount =
+                    Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution);
+            float[] maxAmplitudes = new float[amplitudeCount];
+
+            // Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this
+            // will fail if the loop below is broken and do not replace filled values with actual
+            // vibrator measurements.
+            Arrays.fill(maxAmplitudes, Float.MAX_VALUE);
+
+            for (VibratorInfo info : infos) {
+                Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz();
+                float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes();
+                int vibratorStartIdx = Math.round(
+                        (freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution);
+                int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1;
+
+                if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) {
+                    Slog.w(TAG, "Error calculating the intersection of vibrator frequency"
+                            + " profiles: attempted to fetch from vibrator "
+                            + info.getId() + " max amplitude with bad index " + vibratorStartIdx);
+                    return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null);
+                }
+
+                for (int i = 0; i < maxAmplitudes.length; i++) {
+                    maxAmplitudes[i] = Math.min(maxAmplitudes[i],
+                            vibratorMaxAmplitudes[vibratorStartIdx + i]);
+                }
+            }
+
+            return new FrequencyProfile(resonantFreq, freqRange.getLower(),
+                    freqResolution, maxAmplitudes);
+        }
+
+        @Nullable
+        private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos,
+                float frequencyResolution) {
+            Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz();
+            if (firstRange == null) {
+                // If one vibrator is undefined then the intersection is undefined.
+                return null;
+            }
+            float intersectionLower = firstRange.getLower();
+            float intersectionUpper = firstRange.getUpper();
+
+            // Generate the intersection of all vibrator supported ranges, making sure that both
+            // min supported frequencies are aligned w.r.t. the frequency resolution.
+
+            for (int i = 1; i < infos.length; i++) {
+                Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz();
+                if (vibratorRange == null) {
+                    // If one vibrator is undefined then the intersection is undefined.
+                    return null;
+                }
+
+                if ((vibratorRange.getLower() >= intersectionUpper)
+                        || (vibratorRange.getUpper() <= intersectionLower)) {
+                    // If the range and intersection are disjoint then the intersection is undefined
+                    return null;
+                }
+
+                float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower());
+                if ((frequencyDelta % frequencyResolution) > EPSILON) {
+                    // If the intersection is not aligned with one vibrator then it's undefined
+                    return null;
+                }
+
+                intersectionLower = Math.max(intersectionLower, vibratorRange.getLower());
+                intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper());
+            }
+
+            if ((intersectionUpper - intersectionLower) < frequencyResolution) {
+                // If the intersection is empty then it's undefined.
+                return null;
+            }
+
+            return Range.create(intersectionLower, intersectionUpper);
+        }
+    }
+
+    /**
+     * Listener for all vibrators state change.
+     *
+     * <p>This registers a listener to all vibrators to merge the callbacks into a single state
+     * that is set to true if any individual vibrator is also true, and false otherwise.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static class MultiVibratorStateListener {
+        private final Object mLock = new Object();
+        private final Executor mExecutor;
+        private final OnVibratorStateChangedListener mDelegate;
+
+        @GuardedBy("mLock")
+        private final SparseArray<SingleVibratorStateListener> mVibratorListeners =
+                new SparseArray<>();
+
+        @GuardedBy("mLock")
+        private int mInitializedMask;
+        @GuardedBy("mLock")
+        private int mVibratingMask;
+
+        public MultiVibratorStateListener(@NonNull Executor executor,
+                @NonNull OnVibratorStateChangedListener listener) {
+            mExecutor = executor;
+            mDelegate = listener;
+        }
+
+        /** Returns true if at least one listener was registered to an individual vibrator. */
+        public boolean hasRegisteredListeners() {
+            synchronized (mLock) {
+                return mVibratorListeners.size() > 0;
+            }
+        }
+
+        /** Registers a listener to all individual vibrators in {@link VibratorManager}. */
+        public void register(VibratorManager vibratorManager) {
+            int[] vibratorIds = vibratorManager.getVibratorIds();
+            synchronized (mLock) {
+                for (int i = 0; i < vibratorIds.length; i++) {
+                    int vibratorId = vibratorIds[i];
+                    SingleVibratorStateListener listener = new SingleVibratorStateListener(this, i);
+                    try {
+                        vibratorManager.getVibrator(vibratorId).addVibratorStateListener(mExecutor,
+                                listener);
+                        mVibratorListeners.put(vibratorId, listener);
+                    } catch (RuntimeException e) {
+                        try {
+                            unregister(vibratorManager);
+                        } catch (RuntimeException e1) {
+                            Log.w(TAG,
+                                    "Failed to unregister listener while recovering from a failed "
+                                            + "register call", e1);
+                        }
+                        throw e;
+                    }
+                }
+            }
+        }
+
+        /** Unregisters the listeners from all individual vibrators in {@link VibratorManager}. */
+        public void unregister(VibratorManager vibratorManager) {
+            synchronized (mLock) {
+                for (int i = mVibratorListeners.size(); --i >= 0; ) {
+                    int vibratorId = mVibratorListeners.keyAt(i);
+                    SingleVibratorStateListener listener = mVibratorListeners.valueAt(i);
+                    vibratorManager.getVibrator(vibratorId).removeVibratorStateListener(listener);
+                    mVibratorListeners.removeAt(i);
+                }
+            }
+        }
+
+        /** Callback triggered by {@link SingleVibratorStateListener} for each vibrator. */
+        public void onVibrating(int vibratorIdx, boolean vibrating) {
+            mExecutor.execute(() -> {
+                boolean shouldNotifyStateChange;
+                boolean isAnyVibrating;
+                synchronized (mLock) {
+                    // Bitmask indicating that all vibrators have been initialized.
+                    int allInitializedMask = (1 << mVibratorListeners.size()) - 1;
+
+                    // Save current global state before processing this vibrator state change.
+                    boolean previousIsAnyVibrating = (mVibratingMask != 0);
+                    boolean previousAreAllInitialized = (mInitializedMask == allInitializedMask);
+
+                    // Mark this vibrator as initialized.
+                    int vibratorMask = (1 << vibratorIdx);
+                    mInitializedMask |= vibratorMask;
+
+                    // Flip the vibrating bit flag for this vibrator, only if the state is changing.
+                    boolean previousVibrating = (mVibratingMask & vibratorMask) != 0;
+                    if (previousVibrating != vibrating) {
+                        mVibratingMask ^= vibratorMask;
+                    }
+
+                    // Check new global state after processing this vibrator state change.
+                    isAnyVibrating = (mVibratingMask != 0);
+                    boolean areAllInitialized = (mInitializedMask == allInitializedMask);
+
+                    // Prevent multiple triggers with the same state.
+                    // Trigger once when all vibrators have reported their state, and then only when
+                    // the merged vibrating state changes.
+                    boolean isStateChanging = (previousIsAnyVibrating != isAnyVibrating);
+                    shouldNotifyStateChange =
+                            areAllInitialized && (!previousAreAllInitialized || isStateChanging);
+                }
+                // Notify delegate listener outside the lock, only if merged state is changing.
+                if (shouldNotifyStateChange) {
+                    mDelegate.onVibratorStateChanged(isAnyVibrating);
+                }
+            });
+        }
+    }
+}
diff --git a/android-34/android/os/SystemVibratorManager.java b/android-34/android/os/SystemVibratorManager.java
new file mode 100644
index 0000000..eb2a712
--- /dev/null
+++ b/android-34/android/os/SystemVibratorManager.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * VibratorManager implementation that controls the system vibrators.
+ *
+ * @hide
+ */
+public class SystemVibratorManager extends VibratorManager {
+    private static final String TAG = "VibratorManager";
+
+    private final IVibratorManagerService mService;
+    private final Context mContext;
+    private final Binder mToken = new Binder();
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private int[] mVibratorIds;
+    @GuardedBy("mLock")
+    private final SparseArray<Vibrator> mVibrators = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    private final ArrayMap<Vibrator.OnVibratorStateChangedListener,
+            OnVibratorStateChangedListenerDelegate> mListeners = new ArrayMap<>();
+
+    /**
+     * @hide to prevent subclassing from outside of the framework
+     */
+    public SystemVibratorManager(Context context) {
+        super(context);
+        mContext = context;
+        mService = IVibratorManagerService.Stub.asInterface(
+                ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
+    }
+
+    @NonNull
+    @Override
+    public int[] getVibratorIds() {
+        synchronized (mLock) {
+            if (mVibratorIds != null) {
+                return mVibratorIds;
+            }
+            try {
+                if (mService == null) {
+                    Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager service.");
+                } else {
+                    return mVibratorIds = mService.getVibratorIds();
+                }
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+            return new int[0];
+        }
+    }
+
+    @NonNull
+    @Override
+    public Vibrator getVibrator(int vibratorId) {
+        synchronized (mLock) {
+            Vibrator vibrator = mVibrators.get(vibratorId);
+            if (vibrator != null) {
+                return vibrator;
+            }
+            VibratorInfo info = null;
+            try {
+                if (mService == null) {
+                    Log.w(TAG, "Failed to retrieve vibrator; no vibrator manager service.");
+                } else {
+                    info = mService.getVibratorInfo(vibratorId);
+                }
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+            if (info != null) {
+                vibrator = new SingleVibrator(info);
+                mVibrators.put(vibratorId, vibrator);
+            } else {
+                vibrator = NullVibrator.getInstance();
+            }
+            return vibrator;
+        }
+    }
+
+    @NonNull
+    @Override
+    public Vibrator getDefaultVibrator() {
+        return mContext.getSystemService(Vibrator.class);
+    }
+
+    @Override
+    public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+            @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes) {
+        if (mService == null) {
+            Log.w(TAG, "Failed to set always-on effect; no vibrator manager service.");
+            return false;
+        }
+        try {
+            return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, attributes);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to set always-on effect.", e);
+        }
+        return false;
+    }
+
+    @Override
+    public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
+            String reason, @Nullable VibrationAttributes attributes) {
+        if (mService == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
+            return;
+        }
+        try {
+            mService.vibrate(uid, mContext.getAssociatedDisplayId(), opPkg, effect, attributes,
+                    reason, mToken);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to vibrate.", e);
+        }
+    }
+
+    @Override
+    public void cancel() {
+        cancelVibration(VibrationAttributes.USAGE_FILTER_MATCH_ALL);
+    }
+
+    @Override
+    public void cancel(int usageFilter) {
+        cancelVibration(usageFilter);
+    }
+
+    private void cancelVibration(int usageFilter) {
+        if (mService == null) {
+            Log.w(TAG, "Failed to cancel vibration; no vibrator manager service.");
+            return;
+        }
+        try {
+            mService.cancelVibrate(usageFilter, mToken);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to cancel vibration.", e);
+        }
+    }
+
+    /** Listener for vibrations on a single vibrator. */
+    private static class OnVibratorStateChangedListenerDelegate extends
+            IVibratorStateListener.Stub {
+        private final Executor mExecutor;
+        private final Vibrator.OnVibratorStateChangedListener mListener;
+
+        OnVibratorStateChangedListenerDelegate(
+                @NonNull Vibrator.OnVibratorStateChangedListener listener,
+                @NonNull Executor executor) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onVibrating(boolean isVibrating) {
+            mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+        }
+    }
+
+    /** Controls vibrations on a single vibrator. */
+    private final class SingleVibrator extends Vibrator {
+        private final VibratorInfo mVibratorInfo;
+
+        SingleVibrator(@NonNull VibratorInfo vibratorInfo) {
+            mVibratorInfo = vibratorInfo;
+        }
+
+        @Override
+        protected VibratorInfo getInfo() {
+            return mVibratorInfo;
+        }
+
+        @Override
+        public boolean hasVibrator() {
+            return true;
+        }
+
+        @Override
+        public boolean hasAmplitudeControl() {
+            return mVibratorInfo.hasAmplitudeControl();
+        }
+
+        @Override
+        public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+                @Nullable VibrationEffect effect, @Nullable VibrationAttributes attrs) {
+            CombinedVibration combined = CombinedVibration.startParallel()
+                    .addVibrator(mVibratorInfo.getId(), effect)
+                    .combine();
+            return SystemVibratorManager.this.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined,
+                    attrs);
+        }
+
+        @Override
+        public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason,
+                @NonNull VibrationAttributes attributes) {
+            CombinedVibration combined = CombinedVibration.startParallel()
+                    .addVibrator(mVibratorInfo.getId(), vibe)
+                    .combine();
+            SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes);
+        }
+
+        @Override
+        public void cancel() {
+            SystemVibratorManager.this.cancel();
+        }
+
+        @Override
+        public void cancel(int usageFilter) {
+            SystemVibratorManager.this.cancel(usageFilter);
+        }
+
+        @Override
+        public boolean isVibrating() {
+            if (mService == null) {
+                Log.w(TAG, "Failed to check status of vibrator " + mVibratorInfo.getId()
+                        + "; no vibrator service.");
+                return false;
+            }
+            try {
+                return mService.isVibrating(mVibratorInfo.getId());
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+            return false;
+        }
+
+        @Override
+        public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+            Objects.requireNonNull(listener);
+            if (mContext == null) {
+                Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
+                return;
+            }
+            addVibratorStateListener(mContext.getMainExecutor(), listener);
+        }
+
+        @Override
+        public void addVibratorStateListener(
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnVibratorStateChangedListener listener) {
+            Objects.requireNonNull(listener);
+            Objects.requireNonNull(executor);
+            if (mService == null) {
+                Log.w(TAG,
+                        "Failed to add vibrate state listener to vibrator " + mVibratorInfo.getId()
+                                + "; no vibrator service.");
+                return;
+            }
+            synchronized (mLock) {
+                // If listener is already registered, reject and return.
+                if (mListeners.containsKey(listener)) {
+                    Log.w(TAG, "Listener already registered.");
+                    return;
+                }
+                try {
+                    OnVibratorStateChangedListenerDelegate delegate =
+                            new OnVibratorStateChangedListenerDelegate(listener, executor);
+                    if (!mService.registerVibratorStateListener(mVibratorInfo.getId(), delegate)) {
+                        Log.w(TAG, "Failed to add vibrate state listener to vibrator "
+                                + mVibratorInfo.getId());
+                        return;
+                    }
+                    mListeners.put(listener, delegate);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        @Override
+        public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+            Objects.requireNonNull(listener);
+            if (mService == null) {
+                Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
+                        + mVibratorInfo.getId() + "; no vibrator service.");
+                return;
+            }
+            synchronized (mLock) {
+                // Check if the listener is registered, otherwise will return.
+                if (mListeners.containsKey(listener)) {
+                    OnVibratorStateChangedListenerDelegate delegate = mListeners.get(listener);
+                    try {
+                        if (!mService.unregisterVibratorStateListener(mVibratorInfo.getId(),
+                                delegate)) {
+                            Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
+                                    + mVibratorInfo.getId());
+                            return;
+                        }
+                        mListeners.remove(listener);
+                    } catch (RemoteException e) {
+                        e.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/TelephonyServiceManager.java b/android-34/android/os/TelephonyServiceManager.java
new file mode 100644
index 0000000..6993671
--- /dev/null
+++ b/android-34/android/os/TelephonyServiceManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the telephony
+ * service.
+ *
+ * <p>Only the telephony mainline module will be able to access an instance of this class.
+ *
+ * @hide
+ */
+public class TelephonyServiceManager {
+    /**
+     * @hide
+     */
+    public TelephonyServiceManager() {
+    }
+
+    /**
+     * A class that exposes the methods to register and obtain each system service.
+     */
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Register a system server binding object for a service.
+         */
+        public void register(@NonNull IBinder service) {
+            ServiceManager.addService(mServiceName, service);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it returns null.
+         */
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+         */
+        @NonNull
+        public IBinder getOrThrow() throws ServiceNotFoundException {
+            try {
+                return ServiceManager.getServiceOrThrow(mServiceName);
+            } catch (ServiceManager.ServiceNotFoundException e) {
+                throw new ServiceNotFoundException(mServiceName);
+            }
+        }
+
+        /**
+         * Get the system server binding object for a service. If the specified service is
+         * not available, it returns null.
+         */
+        @Nullable
+        public IBinder tryGet() {
+            return ServiceManager.checkService(mServiceName);
+        }
+    }
+
+    /**
+     * See {@link ServiceRegisterer#getOrThrow}.
+     *
+     * @hide
+     */
+    public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+        /**
+         * Constructor.
+         *
+         * @param name the name of the binder service that cannot be found.
+         *
+         */
+        public ServiceNotFoundException(@NonNull String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "telephony" service.
+     */
+    @NonNull
+    public ServiceRegisterer getTelephonyServiceRegisterer() {
+        return new ServiceRegisterer(Context.TELEPHONY_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the telephony IMS service.
+     */
+    @NonNull
+    public ServiceRegisterer getTelephonyImsServiceRegisterer() {
+        return new ServiceRegisterer(Context.TELEPHONY_IMS_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the telephony RCS message service.
+     */
+    @NonNull
+    public ServiceRegisterer getTelephonyRcsMessageServiceRegisterer() {
+        return new ServiceRegisterer(Context.TELEPHONY_RCS_MESSAGE_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the subscription service.
+     */
+    @NonNull
+    public ServiceRegisterer getSubscriptionServiceRegisterer() {
+        return new ServiceRegisterer("isub");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the phone sub service.
+     */
+    @NonNull
+    public ServiceRegisterer getPhoneSubServiceRegisterer() {
+        return new ServiceRegisterer("iphonesubinfo");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the opportunistic network service.
+     */
+    @NonNull
+    public ServiceRegisterer getOpportunisticNetworkServiceRegisterer() {
+        return new ServiceRegisterer("ions");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the carrier config service.
+     */
+    @NonNull
+    public ServiceRegisterer getCarrierConfigServiceRegisterer() {
+        return new ServiceRegisterer(Context.CARRIER_CONFIG_SERVICE);
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "SMS" service.
+     */
+    @NonNull
+    public ServiceRegisterer getSmsServiceRegisterer() {
+        return new ServiceRegisterer("isms");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the eUICC controller service.
+     */
+    @NonNull
+    public ServiceRegisterer getEuiccControllerService() {
+        return new ServiceRegisterer("econtroller");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the eUICC card controller service.
+     */
+    @NonNull
+    public ServiceRegisterer getEuiccCardControllerServiceRegisterer() {
+        return new ServiceRegisterer("euicc_card_controller");
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the ICC phone book service.
+     */
+    @NonNull
+    public ServiceRegisterer getIccPhoneBookServiceRegisterer() {
+        return new ServiceRegisterer("simphonebook");
+    }
+}
diff --git a/android-34/android/os/Temperature.java b/android-34/android/os/Temperature.java
new file mode 100644
index 0000000..a138431
--- /dev/null
+++ b/android-34/android/os/Temperature.java
@@ -0,0 +1,224 @@
+/*
+ * 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.thermal.TemperatureType;
+import android.hardware.thermal.ThrottlingSeverity;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Temperature values used by IThermalService.
+ *
+ * @hide
+ */
+public final class Temperature implements Parcelable {
+    /** Temperature value */
+    private final float mValue;
+    /** A Temperature type from ThermalHAL */
+    private final int mType;
+    /** Name of this Temperature */
+    private final String mName;
+    /** The level of the sensor is currently in throttling */
+    private final int mStatus;
+
+    @IntDef(prefix = { "THROTTLING_" }, value = {
+            THROTTLING_NONE,
+            THROTTLING_LIGHT,
+            THROTTLING_MODERATE,
+            THROTTLING_SEVERE,
+            THROTTLING_CRITICAL,
+            THROTTLING_EMERGENCY,
+            THROTTLING_SHUTDOWN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ThrottlingStatus {}
+
+    /** Keep in sync with hardware/interfaces/thermal/aidl/android/hardware/thermal
+     * /ThrottlingSeverity.aidl */
+    public static final int THROTTLING_NONE = ThrottlingSeverity.NONE;
+    public static final int THROTTLING_LIGHT = ThrottlingSeverity.LIGHT;
+    public static final int THROTTLING_MODERATE = ThrottlingSeverity.MODERATE;
+    public static final int THROTTLING_SEVERE = ThrottlingSeverity.SEVERE;
+    public static final int THROTTLING_CRITICAL = ThrottlingSeverity.CRITICAL;
+    public static final int THROTTLING_EMERGENCY = ThrottlingSeverity.EMERGENCY;
+    public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
+
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_UNKNOWN,
+            TYPE_CPU,
+            TYPE_GPU,
+            TYPE_BATTERY,
+            TYPE_SKIN,
+            TYPE_USB_PORT,
+            TYPE_POWER_AMPLIFIER,
+            TYPE_BCL_VOLTAGE,
+            TYPE_BCL_CURRENT,
+            TYPE_BCL_PERCENTAGE,
+            TYPE_NPU,
+            TYPE_TPU,
+            TYPE_DISPLAY,
+            TYPE_MODEM,
+            TYPE_SOC
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /** Keep in sync with hardware/interfaces/thermal/aidl/android/hardware/thermal
+     * /TemperatureType.aidl */
+    public static final int TYPE_UNKNOWN = TemperatureType.UNKNOWN;
+    public static final int TYPE_CPU = TemperatureType.CPU;
+    public static final int TYPE_GPU = TemperatureType.GPU;
+    public static final int TYPE_BATTERY = TemperatureType.BATTERY;
+    public static final int TYPE_SKIN = TemperatureType.SKIN;
+    public static final int TYPE_USB_PORT = TemperatureType.USB_PORT;
+    public static final int TYPE_POWER_AMPLIFIER = TemperatureType.POWER_AMPLIFIER;
+    public static final int TYPE_BCL_VOLTAGE = TemperatureType.BCL_VOLTAGE;
+    public static final int TYPE_BCL_CURRENT = TemperatureType.BCL_CURRENT;
+    public static final int TYPE_BCL_PERCENTAGE = TemperatureType.BCL_PERCENTAGE;
+    public static final int TYPE_NPU = TemperatureType.NPU;
+    public static final int TYPE_TPU = TemperatureType.TPU;
+    public static final int TYPE_DISPLAY = TemperatureType.DISPLAY;
+    public static final int TYPE_MODEM = TemperatureType.MODEM;
+    public static final int TYPE_SOC = TemperatureType.SOC;
+
+    /**
+     * Verify a valid Temperature type.
+     *
+     * @return true if a Temperature type is valid otherwise false.
+     */
+    public static boolean isValidType(@Type int type) {
+        return type >= TYPE_UNKNOWN && type <= TYPE_SOC;
+    }
+
+    /**
+     * Verify a valid throttling status.
+     *
+     * @return true if a status is valid otherwise false.
+     */
+    public static boolean isValidStatus(@ThrottlingStatus int status) {
+        return status >= THROTTLING_NONE && status <= THROTTLING_SHUTDOWN;
+    }
+
+    public Temperature(float value, @Type int type,
+            @NonNull String name, @ThrottlingStatus int status) {
+        Preconditions.checkArgument(isValidType(type), "Invalid Type");
+        Preconditions.checkArgument(isValidStatus(status) , "Invalid Status");
+        mValue = value;
+        mType = type;
+        mName = Preconditions.checkStringNotEmpty(name);
+        mStatus = status;
+    }
+
+    /**
+     * Return the Temperature value.
+     *
+     * @return a Temperature value in floating point could be NaN.
+     */
+    public float getValue() {
+        return mValue;
+    }
+
+    /**
+     * Return the Temperature type.
+     *
+     * @return a Temperature type: TYPE_*
+     */
+    public @Type int getType() {
+        return mType;
+    }
+
+    /**
+     * Return the Temperature name.
+     *
+     * @return a Temperature name as String.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Return the Temperature throttling status.
+     *
+     * @return a Temperature throttling status: THROTTLING_*
+     */
+    public @ThrottlingStatus int getStatus() {
+        return mStatus;
+    }
+
+    @Override
+    public String toString() {
+        return "Temperature{mValue=" + mValue + ", mType=" + mType
+                + ", mName=" + mName + ", mStatus=" + mStatus + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mName.hashCode();
+        hash = 31 * hash + Float.hashCode(mValue);
+        hash = 31 * hash + mType;
+        hash = 31 * hash + mStatus;
+        return hash;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (!(o instanceof Temperature)) {
+            return false;
+        }
+        Temperature other = (Temperature) o;
+        return other.mValue == mValue && other.mType == mType
+                && other.mName.equals(mName) && other.mStatus == mStatus;
+    }
+
+    @Override
+    public void writeToParcel(Parcel p, int flags) {
+        p.writeFloat(mValue);
+        p.writeInt(mType);
+        p.writeString(mName);
+        p.writeInt(mStatus);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<Temperature> CREATOR =
+            new Parcelable.Creator<Temperature>() {
+                @Override
+                public Temperature createFromParcel(Parcel p) {
+                    float value = p.readFloat();
+                    int type = p.readInt();
+                    String name = p.readString();
+                    int status = p.readInt();
+                    return new Temperature(value, type, name, status);
+                }
+
+                @Override
+                public Temperature[] newArray(int size) {
+                    return new Temperature[size];
+                }
+
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-34/android/os/TestLooperManager.java b/android-34/android/os/TestLooperManager.java
new file mode 100644
index 0000000..5e7549f
--- /dev/null
+++ b/android-34/android/os/TestLooperManager.java
@@ -0,0 +1,215 @@
+/*
+ * 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.os;
+
+import android.util.ArraySet;
+
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Blocks a looper from executing any messages, and allows the holder of this object
+ * to control when and which messages get executed until it is released.
+ * <p>
+ * A TestLooperManager should be acquired using
+ * {@link android.app.Instrumentation#acquireLooperManager}. Until {@link #release()} is called,
+ * the Looper thread will not execute any messages except when {@link #execute(Message)} is called.
+ * The test code may use {@link #next()} to acquire messages that have been queued to this
+ * {@link Looper}/{@link MessageQueue} and then {@link #execute} to run any that desires.
+ */
+public class TestLooperManager {
+
+    private static final ArraySet<Looper> sHeldLoopers = new ArraySet<>();
+
+    private final MessageQueue mQueue;
+    private final Looper mLooper;
+    private final LinkedBlockingQueue<MessageExecution> mExecuteQueue = new LinkedBlockingQueue<>();
+
+    private boolean mReleased;
+    private boolean mLooperBlocked;
+
+    /**
+     * @hide
+     */
+    public TestLooperManager(Looper looper) {
+        synchronized (sHeldLoopers) {
+            if (sHeldLoopers.contains(looper)) {
+                throw new RuntimeException("TestLooperManager already held for this looper");
+            }
+            sHeldLoopers.add(looper);
+        }
+        mLooper = looper;
+        mQueue = mLooper.getQueue();
+        // Post a message that will keep the looper blocked as long as we are dispatching.
+        new Handler(looper).post(new LooperHolder());
+    }
+
+    /**
+     * Returns the {@link MessageQueue} this object is wrapping.
+     */
+    public MessageQueue getMessageQueue() {
+        checkReleased();
+        return mQueue;
+    }
+
+    /** @removed */
+    @Deprecated
+    public MessageQueue getQueue() {
+        return getMessageQueue();
+    }
+
+    /**
+     * Returns the next message that should be executed by this queue, may block
+     * if no messages are ready.
+     * <p>
+     * Callers should always call {@link #recycle(Message)} on the message when all
+     * interactions with it have completed.
+     */
+    public Message next() {
+        // Wait for the looper block to come up, to make sure we don't accidentally get
+        // the message for the block.
+        while (!mLooperBlocked) {
+            synchronized (this) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        checkReleased();
+        return mQueue.next();
+    }
+
+    /**
+     * Releases the looper to continue standard looping and processing of messages,
+     * no further interactions with TestLooperManager will be allowed after
+     * release() has been called.
+     */
+    public void release() {
+        synchronized (sHeldLoopers) {
+            sHeldLoopers.remove(mLooper);
+        }
+        checkReleased();
+        mReleased = true;
+        mExecuteQueue.add(new MessageExecution());
+    }
+
+    /**
+     * Executes the given message on the Looper thread this wrapper is
+     * attached to.
+     * <p>
+     * Execution will happen on the Looper's thread (whether it is the current thread
+     * or not), but all RuntimeExceptions encountered while executing the message will
+     * be thrown on the calling thread.
+     */
+    public void execute(Message message) {
+        checkReleased();
+        if (Looper.myLooper() == mLooper) {
+            // This is being called from the thread it should be executed on, we can just dispatch.
+            message.target.dispatchMessage(message);
+        } else {
+            MessageExecution execution = new MessageExecution();
+            execution.m = message;
+            synchronized (execution) {
+                mExecuteQueue.add(execution);
+                // Wait for the message to be executed.
+                try {
+                    execution.wait();
+                } catch (InterruptedException e) {
+                }
+                if (execution.response != null) {
+                    throw new RuntimeException(execution.response);
+                }
+            }
+        }
+    }
+
+    /**
+     * Called to indicate that a Message returned by {@link #next()} has been parsed
+     * and should be recycled.
+     */
+    public void recycle(Message msg) {
+        checkReleased();
+        msg.recycleUnchecked();
+    }
+
+    /**
+     * Returns true if there are any queued messages that match the parameters.
+     *
+     * @param h      the value of {@link Message#getTarget()}
+     * @param what   the value of {@link Message#what}
+     * @param object the value of {@link Message#obj}, null for any
+     */
+    public boolean hasMessages(Handler h, Object object, int what) {
+        checkReleased();
+        return mQueue.hasMessages(h, what, object);
+    }
+
+    /**
+     * Returns true if there are any queued messages that match the parameters.
+     *
+     * @param h      the value of {@link Message#getTarget()}
+     * @param r      the value of {@link Message#getCallback()}
+     * @param object the value of {@link Message#obj}, null for any
+     */
+    public boolean hasMessages(Handler h, Object object, Runnable r) {
+        checkReleased();
+        return mQueue.hasMessages(h, r, object);
+    }
+
+    private void checkReleased() {
+        if (mReleased) {
+            throw new RuntimeException("release() has already be called");
+        }
+    }
+
+    private class LooperHolder implements Runnable {
+        @Override
+        public void run() {
+            synchronized (TestLooperManager.this) {
+                mLooperBlocked = true;
+                TestLooperManager.this.notify();
+            }
+            while (!mReleased) {
+                try {
+                    final MessageExecution take = mExecuteQueue.take();
+                    if (take.m != null) {
+                        processMessage(take);
+                    }
+                } catch (InterruptedException e) {
+                }
+            }
+            synchronized (TestLooperManager.this) {
+                mLooperBlocked = false;
+            }
+        }
+
+        private void processMessage(MessageExecution mex) {
+            synchronized (mex) {
+                try {
+                    mex.m.target.dispatchMessage(mex.m);
+                    mex.response = null;
+                } catch (Throwable t) {
+                    mex.response = t;
+                }
+                mex.notifyAll();
+            }
+        }
+    }
+
+    private static class MessageExecution {
+        private Message m;
+        private Throwable response;
+    }
+}
diff --git a/android-34/android/os/ThreadLocalWorkSource.java b/android-34/android/os/ThreadLocalWorkSource.java
new file mode 100644
index 0000000..e9adb20
--- /dev/null
+++ b/android-34/android/os/ThreadLocalWorkSource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Tracks who triggered the work currently executed on this thread.
+ *
+ * <p>ThreadLocalWorkSource is automatically updated inside system server for incoming/outgoing
+ * binder calls and messages posted to handler threads.
+ *
+ * <p>ThreadLocalWorkSource can also be set manually if needed to refine the WorkSource.
+ *
+ * <p>Example:
+ * <ul>
+ * <li>Bluetooth process calls {@link PowerManager#isInteractive()} API on behalf of app foo.
+ * <li>ThreadLocalWorkSource will be automatically set to the UID of foo.
+ * <li>Any code on the thread handling {@link PowerManagerService#isInteractive()} can call
+ * {@link ThreadLocalWorkSource#getUid()} to blame any resource used to handle this call.
+ * <li>If a message is posted from the binder thread, the code handling the message can also call
+ * {@link ThreadLocalWorkSource#getUid()} and it will return the UID of foo since the work source is
+ * automatically propagated.
+ * </ul>
+ *
+ * @hide Only for use within system server.
+ */
+public final class ThreadLocalWorkSource {
+    public static final int UID_NONE = Message.UID_NONE;
+    private static final ThreadLocal<int []> sWorkSourceUid =
+            ThreadLocal.withInitial(() -> new int[] {UID_NONE});
+
+    /**
+     * Returns the UID to blame for the code currently executed on this thread.
+     *
+     * <p>This UID is set automatically by common frameworks (e.g. Binder and Handler frameworks)
+     * and automatically propagated inside system server.
+     * <p>It can also be set manually using {@link #setUid(int)}.
+     */
+    public static int getUid() {
+        return sWorkSourceUid.get()[0];
+    }
+
+    /**
+     * Sets the UID to blame for the code currently executed on this thread.
+     *
+     * <p>Inside system server, this UID will be automatically propagated.
+     * <p>It will be used to attribute future resources used on this thread (e.g. binder
+     * transactions or processing handler messages) and on any other threads the UID is propagated
+     * to.
+     *
+     * @return a token that can be used to restore the state.
+     */
+    public static long setUid(int uid) {
+        final long token = getToken();
+        sWorkSourceUid.get()[0] = uid;
+        return token;
+    }
+
+    /**
+     * Restores the state using the provided token.
+     */
+    public static void restore(long token) {
+        sWorkSourceUid.get()[0] = parseUidFromToken(token);
+    }
+
+    /**
+     * Clears the stored work source uid.
+     *
+     * <p>This method should be used when we do not know who to blame. If the UID to blame is the
+     * UID of the current process, it is better to attribute the work to the current process
+     * explicitly instead of clearing the work source:
+     *
+     * <pre>
+     * ThreadLocalWorkSource.setUid(Process.myUid());
+     * </pre>
+     *
+     * @return a token that can be used to restore the state.
+     */
+    public static long clear() {
+        return setUid(UID_NONE);
+    }
+
+    private static int parseUidFromToken(long token) {
+        return (int) token;
+    }
+
+    private static long getToken() {
+        return sWorkSourceUid.get()[0];
+    }
+
+    private ThreadLocalWorkSource() {
+    }
+}
diff --git a/android-34/android/os/TimestampedValue.java b/android-34/android/os/TimestampedValue.java
new file mode 100644
index 0000000..3d8a550
--- /dev/null
+++ b/android-34/android/os/TimestampedValue.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Objects;
+
+/**
+ * A value with an associated reference time. The reference time will typically be provided by the
+ * elapsed realtime clock. The elapsed realtime clock can be obtained using methods like
+ * {@link SystemClock#elapsedRealtime()} or {@link SystemClock#elapsedRealtimeClock()}.
+ * If a suitable clock is used the reference time can be used to identify the age of a value or
+ * ordering between values.
+ *
+ * <p>This class implements {@link Parcelable} for convenience but instances will only actually be
+ * parcelable if the value type held is {@code null}, {@link Parcelable}, or one of the other types
+ * supported by {@link Parcel#writeValue(Object)} / {@link Parcel#readValue(ClassLoader)}.
+ *
+ * @param <T> the type of the value with an associated timestamp
+ * @hide
+ */
+public final class TimestampedValue<T> implements Parcelable {
+    private final long mReferenceTimeMillis;
+    @Nullable
+    private final T mValue;
+
+    public TimestampedValue(long referenceTimeMillis, @Nullable T value) {
+        mReferenceTimeMillis = referenceTimeMillis;
+        mValue = value;
+    }
+
+    /** Returns the reference time value. See {@link TimestampedValue} for more information. */
+    public long getReferenceTimeMillis() {
+        return mReferenceTimeMillis;
+    }
+
+    /**
+     * Returns the value associated with the timestamp. See {@link TimestampedValue} for more
+     * information.
+     */
+    @Nullable
+    public T getValue() {
+        return mValue;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TimestampedValue<?> that = (TimestampedValue<?>) o;
+        return mReferenceTimeMillis == that.mReferenceTimeMillis
+                && Objects.equals(mValue, that.mValue);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mReferenceTimeMillis, mValue);
+    }
+
+    @Override
+    public String toString() {
+        return "TimestampedValue{"
+                + "mReferenceTimeMillis=" + mReferenceTimeMillis
+                + ", mValue=" + mValue
+                + '}';
+    }
+
+    /**
+     * Returns the difference in milliseconds between two instance's reference times.
+     */
+    public static long referenceTimeDifference(
+            @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
+        return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
+    }
+
+    /** @hide */
+    public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
+            new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
+
+                @Override
+                public TimestampedValue<?> createFromParcel(@NonNull Parcel source) {
+                    return createFromParcel(source, null);
+                }
+
+                @Override
+                public TimestampedValue<?> createFromParcel(
+                        @NonNull Parcel source, @Nullable ClassLoader classLoader) {
+                    long referenceTimeMillis = source.readLong();
+                    Object value = source.readValue(classLoader);
+                    return new TimestampedValue<>(referenceTimeMillis, value);
+                }
+
+                @Override
+                public TimestampedValue[] newArray(int size) {
+                    return new TimestampedValue[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mReferenceTimeMillis);
+        dest.writeValue(mValue);
+    }
+}
diff --git a/android-34/android/os/TokenWatcher.java b/android-34/android/os/TokenWatcher.java
new file mode 100644
index 0000000..00333da
--- /dev/null
+++ b/android-34/android/os/TokenWatcher.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * A TokenWatcher watches a collection of {@link IBinder}s. IBinders are added
+ * to the collection by calling {@link #acquire}, and removed by calling {@link
+ * #release}. IBinders are also implicitly removed when they become weakly
+ * reachable. Each IBinder may be added at most once.
+ *
+ * The {@link #acquired} method is invoked by posting to the specified handler
+ * whenever the size of the watched collection becomes nonzero.  The {@link
+ * #released} method is invoked on the specified handler whenever the size of
+ * the watched collection becomes zero.
+ */
+public abstract class TokenWatcher
+{
+    /**
+     * Construct the TokenWatcher
+     *
+     * @param h A handler to call {@link #acquired} and {@link #released}
+     * on.  If you don't care, just call it like this, although your thread
+     * will have to be a Looper thread.
+     * <code>new TokenWatcher(new Handler())</code>
+     * @param tag A debugging tag for this TokenWatcher
+     */
+    public TokenWatcher(Handler h, String tag)
+    {
+        mHandler = h;
+        mTag = tag != null ? tag : "TokenWatcher";
+    }
+
+    /**
+     * Called when the number of active tokens goes from 0 to 1.
+     */
+    public abstract void acquired();
+
+    /**
+     * Called when the number of active tokens goes from 1 to 0.
+     */
+    public abstract void released();
+
+    /**
+     * Record that this token has been acquired.  When acquire is called, and
+     * the current count is 0, the acquired method is called on the given
+     * handler.
+     *
+     * Note that the same {@code token} can only be acquired once. If this
+     * {@code token} has already been acquired, no action is taken. The first
+     * subsequent call to {@link #release} will release this {@code token}
+     * immediately.
+     *
+     * @param token An IBinder object.
+     * @param tag   A string used by the {@link #dump} method for debugging,
+     *              to see who has references.
+     */
+    public void acquire(IBinder token, String tag)
+    {
+        synchronized (mTokens) {
+            if (mTokens.containsKey(token)) {
+                return;
+            }
+
+            // explicitly checked to avoid bogus sendNotification calls because
+            // of the WeakHashMap and the GC
+            int oldSize = mTokens.size();
+
+            Death d = new Death(token, tag);
+            try {
+                token.linkToDeath(d, 0);
+            } catch (RemoteException e) {
+                return;
+            }
+            mTokens.put(token, d);
+
+            if (oldSize == 0 && !mAcquired) {
+                sendNotificationLocked(true);
+                mAcquired = true;
+            }
+        }
+    }
+
+    public void cleanup(IBinder token, boolean unlink)
+    {
+        synchronized (mTokens) {
+            Death d = mTokens.remove(token);
+            if (unlink && d != null) {
+                d.token.unlinkToDeath(d, 0);
+                d.token = null;
+            }
+
+            if (mTokens.size() == 0 && mAcquired) {
+                sendNotificationLocked(false);
+                mAcquired = false;
+            }
+        }
+    }
+
+    public void release(IBinder token)
+    {
+        cleanup(token, true);
+    }
+
+    public boolean isAcquired()
+    {
+        synchronized (mTokens) {
+            return mAcquired;
+        }
+    }
+
+    public void dump()
+    {
+        ArrayList<String> a = dumpInternal();
+        for (String s : a) {
+            Log.i(mTag, s);
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        ArrayList<String> a = dumpInternal();
+        for (String s : a) {
+            pw.println(s);
+        }
+    }
+
+    private ArrayList<String> dumpInternal() {
+        ArrayList<String> a = new ArrayList<String>();
+        synchronized (mTokens) {
+            Set<IBinder> keys = mTokens.keySet();
+            a.add("Token count: " + mTokens.size());
+            int i = 0;
+            for (IBinder b: keys) {
+                a.add("[" + i + "] " + mTokens.get(b).tag + " - " + b);
+                i++;
+            }
+        }
+        return a;
+    }
+
+    private Runnable mNotificationTask = new Runnable() {
+        public void run()
+        {
+            int value;
+            synchronized (mTokens) {
+                value = mNotificationQueue;
+                mNotificationQueue = -1;
+            }
+            if (value == 1) {
+                acquired();
+            }
+            else if (value == 0) {
+                released();
+            }
+        }
+    };
+
+    private void sendNotificationLocked(boolean on)
+    {
+        int value = on ? 1 : 0;
+        if (mNotificationQueue == -1) {
+            // empty
+            mNotificationQueue = value;
+            mHandler.post(mNotificationTask);
+        }
+        else if (mNotificationQueue != value) {
+            // it's a pair, so cancel it
+            mNotificationQueue = -1;
+            mHandler.removeCallbacks(mNotificationTask);
+        }
+        // else, same so do nothing -- maybe we should warn?
+    }
+
+    private class Death implements IBinder.DeathRecipient
+    {
+        IBinder token;
+        String tag;
+
+        Death(IBinder token, String tag)
+        {
+            this.token = token;
+            this.tag = tag;
+        }
+
+        public void binderDied()
+        {
+            cleanup(token, false);
+        }
+
+        protected void finalize() throws Throwable
+        {
+            try {
+                if (token != null) {
+                    Log.w(mTag, "cleaning up leaked reference: " + tag);
+                    release(token);
+                }
+            }
+            finally {
+                super.finalize();
+            }
+        }
+    }
+
+    private WeakHashMap<IBinder,Death> mTokens = new WeakHashMap<IBinder,Death>();
+    private Handler mHandler;
+    private String mTag;
+    private int mNotificationQueue = -1;
+    private volatile boolean mAcquired = false;
+}
diff --git a/android-34/android/os/Trace.java b/android-34/android/os/Trace.java
new file mode 100644
index 0000000..0d0d1da
--- /dev/null
+++ b/android-34/android/os/Trace.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+/**
+ * Writes trace events to the system trace buffer.  These trace events can be
+ * collected and visualized using the Systrace tool.
+ *
+ * <p>This tracing mechanism is independent of the method tracing mechanism
+ * offered by {@link Debug#startMethodTracing}.  In particular, it enables
+ * tracing of events that occur across multiple processes.
+ * <p>For information about using the Systrace tool, read <a
+ * href="{@docRoot}tools/debugging/systrace.html">Analyzing Display and Performance
+ * with Systrace</a>.
+ */
+public final class Trace {
+    /*
+     * Writes trace events to the kernel trace buffer.  These trace events can be
+     * collected using the "atrace" program for offline analysis.
+     */
+
+    private static final String TAG = "Trace";
+
+    // These tags must be kept in sync with system/core/include/cutils/trace.h.
+    // They should also be added to frameworks/native/cmds/atrace/atrace.cpp.
+    /** @hide */
+    public static final long TRACE_TAG_NEVER = 0;
+    /** @hide */
+    public static final long TRACE_TAG_ALWAYS = 1L << 0;
+    /** @hide */
+    public static final long TRACE_TAG_GRAPHICS = 1L << 1;
+    /** @hide */
+    public static final long TRACE_TAG_INPUT = 1L << 2;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final long TRACE_TAG_VIEW = 1L << 3;
+    /** @hide */
+    public static final long TRACE_TAG_WEBVIEW = 1L << 4;
+    /** @hide */
+    public static final long TRACE_TAG_WINDOW_MANAGER = 1L << 5;
+    /** @hide */
+    public static final long TRACE_TAG_ACTIVITY_MANAGER = 1L << 6;
+    /** @hide */
+    public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7;
+    /** @hide */
+    public static final long TRACE_TAG_AUDIO = 1L << 8;
+    /** @hide */
+    public static final long TRACE_TAG_VIDEO = 1L << 9;
+    /** @hide */
+    public static final long TRACE_TAG_CAMERA = 1L << 10;
+    /** @hide */
+    public static final long TRACE_TAG_HAL = 1L << 11;
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final long TRACE_TAG_APP = 1L << 12;
+    /** @hide */
+    public static final long TRACE_TAG_RESOURCES = 1L << 13;
+    /** @hide */
+    public static final long TRACE_TAG_DALVIK = 1L << 14;
+    /** @hide */
+    public static final long TRACE_TAG_RS = 1L << 15;
+    /** @hide */
+    public static final long TRACE_TAG_BIONIC = 1L << 16;
+    /** @hide */
+    public static final long TRACE_TAG_POWER = 1L << 17;
+    /** @hide */
+    public static final long TRACE_TAG_PACKAGE_MANAGER = 1L << 18;
+    /** @hide */
+    public static final long TRACE_TAG_SYSTEM_SERVER = 1L << 19;
+    /** @hide */
+    public static final long TRACE_TAG_DATABASE = 1L << 20;
+    /** @hide */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final long TRACE_TAG_NETWORK = 1L << 21;
+    /** @hide */
+    public static final long TRACE_TAG_ADB = 1L << 22;
+    /** @hide */
+    public static final long TRACE_TAG_VIBRATOR = 1L << 23;
+    /** @hide */
+    @SystemApi
+    public static final long TRACE_TAG_AIDL = 1L << 24;
+    /** @hide */
+    public static final long TRACE_TAG_NNAPI = 1L << 25;
+    /** @hide */
+    public static final long TRACE_TAG_RRO = 1L << 26;
+    /** @hide */
+    public static final long TRACE_TAG_THERMAL = 1L << 27;
+
+    private static final long TRACE_TAG_NOT_READY = 1L << 63;
+    /** @hide **/
+    public static final int MAX_SECTION_NAME_LEN = 127;
+
+    // Must be volatile to avoid word tearing.
+    // This is only kept in case any apps get this by reflection but do not
+    // check the return value for null.
+    @UnsupportedAppUsage
+    private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
+
+    private static int sZygoteDebugFlags = 0;
+
+    @UnsupportedAppUsage
+    @CriticalNative
+    private static native long nativeGetEnabledTags();
+    private static native void nativeSetAppTracingAllowed(boolean allowed);
+    private static native void nativeSetTracingEnabled(boolean allowed);
+
+    @FastNative
+    private static native void nativeTraceCounter(long tag, String name, long value);
+    @FastNative
+    private static native void nativeTraceBegin(long tag, String name);
+    @FastNative
+    private static native void nativeTraceEnd(long tag);
+    @FastNative
+    private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
+    @FastNative
+    private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
+    @FastNative
+    private static native void nativeAsyncTraceForTrackBegin(long tag,
+            String trackName, String name, int cookie);
+    @FastNative
+    private static native void nativeAsyncTraceForTrackEnd(long tag,
+            String trackName, int cookie);
+    @FastNative
+    private static native void nativeInstant(long tag, String name);
+    @FastNative
+    private static native void nativeInstantForTrack(long tag, String trackName, String name);
+
+    private Trace() {
+    }
+
+    /**
+     * Returns true if a trace tag is enabled.
+     *
+     * @param traceTag The trace tag to check.
+     * @return True if the trace tag is valid.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static boolean isTagEnabled(long traceTag) {
+        long tags = nativeGetEnabledTags();
+        return (tags & traceTag) != 0;
+    }
+
+    /**
+     * Writes trace message to indicate the value of a given counter.
+     *
+     * @param traceTag The trace tag.
+     * @param counterName The counter name to appear in the trace.
+     * @param counterValue The counter value.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void traceCounter(long traceTag, @NonNull String counterName, int counterValue) {
+        if (isTagEnabled(traceTag)) {
+            nativeTraceCounter(traceTag, counterName, counterValue);
+        }
+    }
+
+    /**
+     * From Android S, this is no-op.
+     *
+     * Before, set whether application tracing is allowed for this process.  This is intended to be
+     * set once at application start-up time based on whether the application is debuggable.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static void setAppTracingAllowed(boolean allowed) {
+        nativeSetAppTracingAllowed(allowed);
+    }
+
+    /**
+     * Set whether tracing is enabled in this process.
+     * @hide
+     */
+    public static void setTracingEnabled(boolean enabled, int debugFlags) {
+        nativeSetTracingEnabled(enabled);
+        sZygoteDebugFlags = debugFlags;
+    }
+
+    /**
+     * Writes a trace message to indicate that a given section of code has
+     * begun. Must be followed by a call to {@link #traceEnd} using the same
+     * tag.
+     *
+     * @param traceTag The trace tag.
+     * @param methodName The method name to appear in the trace.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void traceBegin(long traceTag, @NonNull String methodName) {
+        if (isTagEnabled(traceTag)) {
+            nativeTraceBegin(traceTag, methodName);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that the current method has ended.
+     * Must be called exactly once for each call to {@link #traceBegin} using the same tag.
+     *
+     * @param traceTag The trace tag.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void traceEnd(long traceTag) {
+        if (isTagEnabled(traceTag)) {
+            nativeTraceEnd(traceTag);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that a given section of code has
+     * begun. Must be followed by a call to {@link #asyncTraceEnd} using the same
+     * tag, name and cookie.
+     *
+     * If two events with the same methodName overlap in time then they *must* have
+     * different cookie values. If they do not, the trace can become corrupted
+     * in unpredictable ways.
+     *
+     * Unlike {@link #traceBegin(long, String)} and {@link #traceEnd(long)},
+     * asynchronous events cannot be not nested. Consider using
+     * {@link #asyncTraceForTrackBegin(long, String, String, int)}
+     * if nested asynchronous events are needed.
+     *
+     * @param traceTag The trace tag.
+     * @param methodName The method name to appear in the trace.
+     * @param cookie Unique identifier for distinguishing simultaneous events
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void asyncTraceBegin(long traceTag, @NonNull String methodName, int cookie) {
+        if (isTagEnabled(traceTag)) {
+            nativeAsyncTraceBegin(traceTag, methodName, cookie);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that the current method has ended.
+     * Must be called exactly once for each call to {@link #asyncTraceBegin(long, String, int)}
+     * using the same tag, name and cookie.
+     *
+     * See the documentation for {@link #asyncTraceBegin(long, String, int)}.
+     * for inteded usage of this method.
+     *
+     * @param traceTag The trace tag.
+     * @param methodName The method name to appear in the trace.
+     * @param cookie Unique identifier for distinguishing simultaneous events
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void asyncTraceEnd(long traceTag, @NonNull String methodName, int cookie) {
+        if (isTagEnabled(traceTag)) {
+            nativeAsyncTraceEnd(traceTag, methodName, cookie);
+        }
+    }
+
+
+    /**
+     * Writes a trace message to indicate that a given section of code has
+     * begun. Must be followed by a call to {@link #asyncTraceForTrackEnd} using the same
+     * track name and cookie.
+     *
+     * Events with the same trackName and cookie nest inside each other in the
+     * same way as calls to {@link #traceBegin(long, String)} and
+     * {@link #traceEnd(long)}.
+     *
+     * If two events with the same trackName overlap in time but do not nest
+     * correctly, then they *must* have different cookie values. If they do not,
+     * the trace can become corrupted in unpredictable ways.
+     *
+     * Good Example:
+     *
+     * public void parent() {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "Track", "parent", mId);
+     *   child()
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "Track", mId);
+     * }
+     *
+     * public void child() {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "Track", "child", mId);
+     *   // Some code here.
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "Track", mId);
+     * }
+     *
+     * This would be visualized as so:
+     *   [   Parent   ]
+     *     [ Child ]
+     *
+     * Bad Example:
+     *
+     * public static void processData(String dataToProcess) {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "processDataInParallel", "processData", 0);
+     *   // Some code here.
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "processDataInParallel", 0);
+     * }
+     *
+     * public static void processDataInParallel({@code List<String>} data) {
+     *   ExecutorService executor = Executors.newCachedThreadPool();
+     *   for (String s : data) {
+     *     pool.execute(() -> processData(s));
+     *   }
+     * }
+     *
+     * This is invalid because it's possible for processData to be run many times
+     * in parallel (i.e. processData events overlap) but the same cookie is
+     * provided each time.
+     *
+     * To fix this, specify a different id in each invocation of processData:
+     *
+     * public static void processData(String dataToProcess, int id) {
+     *   asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "processDataInParallel", "processData", id);
+     *   // Some code here.
+     *   asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "processDataInParallel", id);
+     * }
+     *
+     * public static void processDataInParallel({@code List<String>} data) {
+     *   ExecutorService executor = Executors.newCachedThreadPool();
+     *   for (int i = 0; i < data.size(); ++i) {
+     *     pool.execute(() -> processData(data.get(i), i));
+     *   }
+     * }
+     *
+     * @param traceTag The trace tag.
+     * @param trackName The track where the event should appear in the trace.
+     * @param methodName The method name to appear in the trace.
+     * @param cookie Unique identifier used for nesting events on a single
+     *               track. Events which overlap without nesting on the same
+     *               track must have different values for cookie.
+     *
+     * @hide
+     */
+    public static void asyncTraceForTrackBegin(long traceTag,
+            @NonNull String trackName, @NonNull String methodName, int cookie) {
+        if (isTagEnabled(traceTag)) {
+            nativeAsyncTraceForTrackBegin(traceTag, trackName, methodName, cookie);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that the current method has ended.
+     * Must be called exactly once for each call to
+     * {@link #asyncTraceForTrackBegin(long, String, String, int)}
+     * using the same tag, track name, and cookie.
+     *
+     * See the documentation for {@link #asyncTraceForTrackBegin(long, String, String, int)}.
+     * for inteded usage of this method.
+     *
+     * @param traceTag The trace tag.
+     * @param trackName The track where the event should appear in the trace.
+     * @param cookie Unique identifier used for nesting events on a single
+     *               track. Events which overlap without nesting on the same
+     *               track must have different values for cookie.
+     *
+     * @hide
+     */
+    public static void asyncTraceForTrackEnd(long traceTag,
+            @NonNull String trackName, int cookie) {
+        if (isTagEnabled(traceTag)) {
+            nativeAsyncTraceForTrackEnd(traceTag, trackName, cookie);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that a given section of code was invoked.
+     *
+     * @param traceTag The trace tag.
+     * @param methodName The method name to appear in the trace.
+     * @hide
+     */
+    public static void instant(long traceTag, String methodName) {
+        if (isTagEnabled(traceTag)) {
+            nativeInstant(traceTag, methodName);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that a given section of code was invoked.
+     *
+     * @param traceTag The trace tag.
+     * @param trackName The track where the event should appear in the trace.
+     * @param methodName The method name to appear in the trace.
+     * @hide
+     */
+    public static void instantForTrack(long traceTag, String trackName, String methodName) {
+        if (isTagEnabled(traceTag)) {
+            nativeInstantForTrack(traceTag, trackName, methodName);
+        }
+    }
+
+    /**
+     * Checks whether or not tracing is currently enabled. This is useful to avoid intermediate
+     * string creation for trace sections that require formatting. It is not necessary
+     * to guard all Trace method calls as they internally already check this. However it is
+     * recommended to use this to prevent creating any temporary objects that would then be
+     * passed to those methods to reduce runtime cost when tracing isn't enabled.
+     *
+     * @return true if tracing is currently enabled, false otherwise
+     */
+    public static boolean isEnabled() {
+        return isTagEnabled(TRACE_TAG_APP);
+    }
+
+    /**
+     * Writes a trace message to indicate that a given section of code has begun. This call must
+     * be followed by a corresponding call to {@link #endSection()} on the same thread.
+     *
+     * <p class="note"> At this time the vertical bar character '|', newline character '\n', and
+     * null character '\0' are used internally by the tracing mechanism.  If sectionName contains
+     * these characters they will be replaced with a space character in the trace.
+     *
+     * @param sectionName The name of the code section to appear in the trace.  This may be at
+     * most 127 Unicode code units long.
+     */
+    public static void beginSection(@NonNull String sectionName) {
+        if (isTagEnabled(TRACE_TAG_APP)) {
+            if (sectionName.length() > MAX_SECTION_NAME_LEN) {
+                throw new IllegalArgumentException("sectionName is too long");
+            }
+            nativeTraceBegin(TRACE_TAG_APP, sectionName);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that a given section of code has ended. This call must
+     * be preceeded by a corresponding call to {@link #beginSection(String)}. Calling this method
+     * will mark the end of the most recently begun section of code, so care must be taken to
+     * ensure that beginSection / endSection pairs are properly nested and called from the same
+     * thread.
+     */
+    public static void endSection() {
+        if (isTagEnabled(TRACE_TAG_APP)) {
+            nativeTraceEnd(TRACE_TAG_APP);
+        }
+    }
+
+    /**
+     * Writes a trace message to indicate that a given section of code has
+     * begun. Must be followed by a call to {@link #endAsyncSection(String, int)} with the same
+     * methodName and cookie. Unlike {@link #beginSection(String)} and {@link #endSection()},
+     * asynchronous events do not need to be nested. The name and cookie used to
+     * begin an event must be used to end it.
+     *
+     * @param methodName The method name to appear in the trace.
+     * @param cookie Unique identifier for distinguishing simultaneous events
+     */
+    public static void beginAsyncSection(@NonNull String methodName, int cookie) {
+        asyncTraceBegin(TRACE_TAG_APP, methodName, cookie);
+    }
+
+    /**
+     * Writes a trace message to indicate that the current method has ended.
+     * Must be called exactly once for each call to {@link #beginAsyncSection(String, int)}
+     * using the same name and cookie.
+     *
+     * @param methodName The method name to appear in the trace.
+     * @param cookie Unique identifier for distinguishing simultaneous events
+     */
+    public static void endAsyncSection(@NonNull String methodName, int cookie) {
+        asyncTraceEnd(TRACE_TAG_APP, methodName, cookie);
+    }
+
+    /**
+     * Writes trace message to indicate the value of a given counter.
+     *
+     * @param counterName The counter name to appear in the trace.
+     * @param counterValue The counter value.
+     */
+    public static void setCounter(@NonNull String counterName, long counterValue) {
+        if (isTagEnabled(TRACE_TAG_APP)) {
+            nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
+        }
+    }
+}
diff --git a/android-34/android/os/TraceNameSupplier.java b/android-34/android/os/TraceNameSupplier.java
new file mode 100644
index 0000000..e4b3a4e
--- /dev/null
+++ b/android-34/android/os/TraceNameSupplier.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+
+/**
+ * Supplier for custom trace messages.
+ *
+ * @hide
+ */
+public interface TraceNameSupplier {
+
+    /**
+     * Gets the name used for trace messages.
+     */
+    @NonNull String getTraceName();
+}
diff --git a/android-34/android/os/TracePerfTest.java b/android-34/android/os/TracePerfTest.java
new file mode 100644
index 0000000..0d64c39
--- /dev/null
+++ b/android-34/android/os/TracePerfTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.ShellHelper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TracePerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @BeforeClass
+    public static void startTracing() {
+        ShellHelper.runShellCommandRaw("atrace -c --async_start -a *");
+    }
+
+    @AfterClass
+    public static void endTracing() {
+        ShellHelper.runShellCommandRaw("atrace --async_stop");
+    }
+
+    @Before
+    public void verifyTracingEnabled() {
+        Assert.assertTrue(Trace.isEnabled());
+    }
+
+    @Test
+    public void testEnabled() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.isEnabled();
+        }
+    }
+
+    @Test
+    public void testBeginEndSection() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.beginSection("testBeginEndSection");
+            Trace.endSection();
+        }
+    }
+
+    @Test
+    public void testAsyncBeginEnd() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.beginAsyncSection("testAsyncBeginEnd", 42);
+            Trace.endAsyncSection("testAsyncBeginEnd", 42);
+        }
+    }
+
+    @Test
+    public void testCounter() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.setCounter("testCounter", 123);
+        }
+    }
+}
diff --git a/android-34/android/os/TransactionTooLargeException.java b/android-34/android/os/TransactionTooLargeException.java
new file mode 100644
index 0000000..4d5b2a1
--- /dev/null
+++ b/android-34/android/os/TransactionTooLargeException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2011 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.os;
+import android.os.RemoteException;
+
+/**
+ * The Binder transaction failed because it was too large.
+ * <p>
+ * During a remote procedure call, the arguments and the return value of the call
+ * are transferred as {@link Parcel} objects stored in the Binder transaction buffer.
+ * If the arguments or the return value are too large to fit in the transaction buffer,
+ * then the call will fail.  {@link TransactionTooLargeException} is thrown as a
+ * heuristic when a transaction is large, and it fails, since these are the transactions
+ * which are most likely to overfill the transaction buffer.
+ * </p><p>
+ * The Binder transaction buffer has a limited fixed size, currently 1MB, which
+ * is shared by all transactions in progress for the process.  Consequently this
+ * exception can be thrown when there are many transactions in progress even when
+ * most of the individual transactions are of moderate size.
+ * </p><p>
+ * There are two possible outcomes when a remote procedure call throws
+ * {@link TransactionTooLargeException}.  Either the client was unable to send
+ * its request to the service (most likely if the arguments were too large to fit in
+ * the transaction buffer), or the service was unable to send its response back
+ * to the client (most likely if the return value was too large to fit
+ * in the transaction buffer).  It is not possible to tell which of these outcomes
+ * actually occurred.  The client should assume that a partial failure occurred.
+ * </p><p>
+ * The key to avoiding {@link TransactionTooLargeException} is to keep all
+ * transactions relatively small.  Try to minimize the amount of memory needed to create
+ * a {@link Parcel} for the arguments and the return value of the remote procedure call.
+ * Avoid transferring huge arrays of strings or large bitmaps.
+ * If possible, try to break up big requests into smaller pieces.
+ * </p><p>
+ * If you are implementing a service, it may help to impose size or complexity
+ * contraints on the queries that clients can perform.  For example, if the result set
+ * could become large, then don't allow the client to request more than a few records
+ * at a time.  Alternately, instead of returning all of the available data all at once,
+ * return the essential information first and make the client ask for additional information
+ * later as needed.
+ * </p>
+ */
+public class TransactionTooLargeException extends RemoteException {
+    public TransactionTooLargeException() {
+        super();
+    }
+
+    public TransactionTooLargeException(String msg) {
+        super(msg);
+    }
+}
diff --git a/android-34/android/os/TransactionTracker.java b/android-34/android/os/TransactionTracker.java
new file mode 100644
index 0000000..ebb4699
--- /dev/null
+++ b/android-34/android/os/TransactionTracker.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 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.os;
+
+import android.util.Log;
+import android.util.Size;
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class used to track binder transactions. It indexes the transactions by the stack trace.
+ *
+ * @hide
+ */
+public class TransactionTracker {
+    private Map<String, Long> mTraces;
+
+    private void resetTraces() {
+        synchronized (this) {
+            mTraces = new HashMap<String, Long>();
+        }
+    }
+
+    TransactionTracker() {
+        resetTraces();
+    }
+
+    public void addTrace(Throwable tr) {
+        String trace = Log.getStackTraceString(tr);
+        synchronized (this) {
+            if (mTraces.containsKey(trace)) {
+                mTraces.put(trace, mTraces.get(trace) + 1);
+            } else {
+                mTraces.put(trace, Long.valueOf(1));
+            }
+        }
+    }
+
+    public void writeTracesToFile(ParcelFileDescriptor fd) {
+        if (mTraces.isEmpty()) {
+            return;
+        }
+
+        PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
+        synchronized (this) {
+            for (String trace : mTraces.keySet()) {
+                pw.println("Count: " + mTraces.get(trace));
+                pw.println("Trace: " + trace);
+                pw.println();
+            }
+        }
+        pw.flush();
+    }
+
+    public void clearTraces(){
+        resetTraces();
+    }
+}
diff --git a/android-34/android/os/UEventObserver.java b/android-34/android/os/UEventObserver.java
new file mode 100644
index 0000000..fa30e50
--- /dev/null
+++ b/android-34/android/os/UEventObserver.java
@@ -0,0 +1,246 @@
+/*
+ * 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.
+ * 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * UEventObserver is an abstract class that receives UEvents from the kernel.<p>
+ *
+ * Subclass UEventObserver, implementing onUEvent(UEvent event), then call
+ * startObserving() with a match string. The UEvent thread will then call your
+ * onUEvent() method when a UEvent occurs that contains your match string.<p>
+ *
+ * Call stopObserving() to stop receiving UEvents.<p>
+ *
+ * There is only one UEvent thread per process, even if that process has
+ * multiple UEventObserver subclass instances. The UEvent thread starts when
+ * the startObserving() is called for the first time in that process. Once
+ * started the UEvent thread will not stop (although it can stop notifying
+ * UEventObserver's via stopObserving()).<p>
+ *
+ * @hide
+*/
+public abstract class UEventObserver {
+    private static final String TAG = "UEventObserver";
+    private static final boolean DEBUG = false;
+
+    private static UEventThread sThread;
+
+    private static native void nativeSetup();
+    private static native String nativeWaitForNextEvent();
+    private static native void nativeAddMatch(String match);
+    private static native void nativeRemoveMatch(String match);
+
+    @UnsupportedAppUsage
+    public UEventObserver() {
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            stopObserving();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static UEventThread getThread() {
+        synchronized (UEventObserver.class) {
+            if (sThread == null) {
+                sThread = new UEventThread();
+                sThread.start();
+            }
+            return sThread;
+        }
+    }
+
+    private static UEventThread peekThread() {
+        synchronized (UEventObserver.class) {
+            return sThread;
+        }
+    }
+
+    /**
+     * Begin observation of UEvents.<p>
+     * This method will cause the UEvent thread to start if this is the first
+     * invocation of startObserving in this process.<p>
+     * Once called, the UEvent thread will call onUEvent() when an incoming
+     * UEvent matches the specified string.<p>
+     * This method can be called multiple times to register multiple matches.
+     * Only one call to stopObserving is required even with multiple registered
+     * matches.
+     *
+     * @param match A substring of the UEvent to match.  Try to be as specific
+     * as possible to avoid incurring unintended additional cost from processing
+     * irrelevant messages.  Netlink messages can be moderately high bandwidth and
+     * are expensive to parse.  For example, some devices may send one netlink message
+     * for each vsync period.
+     */
+    @UnsupportedAppUsage
+    public final void startObserving(String match) {
+        if (match == null || match.isEmpty()) {
+            throw new IllegalArgumentException("match substring must be non-empty");
+        }
+
+        final UEventThread t = getThread();
+        t.addObserver(match, this);
+    }
+
+    /**
+     * End observation of UEvents.<p>
+     * This process's UEvent thread will never call onUEvent() on this
+     * UEventObserver after this call. Repeated calls have no effect.
+     */
+    @UnsupportedAppUsage
+    public final void stopObserving() {
+        final UEventThread t = peekThread();
+        if (t != null) {
+            t.removeObserver(this);
+        }
+    }
+
+    /**
+     * Subclasses of UEventObserver should override this method to handle
+     * UEvents.
+     */
+    @UnsupportedAppUsage
+    public abstract void onUEvent(UEvent event);
+
+    /**
+     * Representation of a UEvent.
+     */
+    public static final class UEvent {
+        // collection of key=value pairs parsed from the uevent message
+        private final HashMap<String,String> mMap = new HashMap<String,String>();
+
+        public UEvent(String message) {
+            int offset = 0;
+            int length = message.length();
+
+            while (offset < length) {
+                int equals = message.indexOf('=', offset);
+                int at = message.indexOf('\0', offset);
+                if (at < 0) break;
+
+                if (equals > offset && equals < at) {
+                    // key is before the equals sign, and value is after
+                    mMap.put(message.substring(offset, equals),
+                            message.substring(equals + 1, at));
+                }
+
+                offset = at + 1;
+            }
+        }
+
+        @UnsupportedAppUsage
+        public String get(String key) {
+            return mMap.get(key);
+        }
+
+        @UnsupportedAppUsage
+        public String get(String key, String defaultValue) {
+            String result = mMap.get(key);
+            return (result == null ? defaultValue : result);
+        }
+
+        public String toString() {
+            return mMap.toString();
+        }
+    }
+
+    private static final class UEventThread extends Thread {
+        /** Many to many mapping of string match to observer.
+         *  Multimap would be better, but not available in android, so use
+         *  an ArrayList where even elements are the String match and odd
+         *  elements the corresponding UEventObserver observer */
+        private final ArrayList<Object> mKeysAndObservers = new ArrayList<Object>();
+
+        private final ArrayList<UEventObserver> mTempObserversToSignal =
+                new ArrayList<UEventObserver>();
+
+        public UEventThread() {
+            super("UEventObserver");
+        }
+
+        @Override
+        public void run() {
+            nativeSetup();
+
+            while (true) {
+                String message = nativeWaitForNextEvent();
+                if (message != null) {
+                    if (DEBUG) {
+                        Log.d(TAG, message);
+                    }
+                    sendEvent(message);
+                }
+            }
+        }
+
+        private void sendEvent(String message) {
+            synchronized (mKeysAndObservers) {
+                final int N = mKeysAndObservers.size();
+                for (int i = 0; i < N; i += 2) {
+                    final String key = (String)mKeysAndObservers.get(i);
+                    if (message.contains(key)) {
+                        final UEventObserver observer =
+                                (UEventObserver)mKeysAndObservers.get(i + 1);
+                        mTempObserversToSignal.add(observer);
+                    }
+                }
+            }
+
+            if (!mTempObserversToSignal.isEmpty()) {
+                final UEvent event = new UEvent(message);
+                final int N = mTempObserversToSignal.size();
+                for (int i = 0; i < N; i++) {
+                    final UEventObserver observer = mTempObserversToSignal.get(i);
+                    observer.onUEvent(event);
+                }
+                mTempObserversToSignal.clear();
+            }
+        }
+
+        public void addObserver(String match, UEventObserver observer) {
+            synchronized (mKeysAndObservers) {
+                mKeysAndObservers.add(match);
+                mKeysAndObservers.add(observer);
+                nativeAddMatch(match);
+            }
+        }
+
+        /** Removes every key/value pair where value=observer from mObservers */
+        public void removeObserver(UEventObserver observer) {
+            synchronized (mKeysAndObservers) {
+                for (int i = 0; i < mKeysAndObservers.size(); ) {
+                    if (mKeysAndObservers.get(i + 1) == observer) {
+                        mKeysAndObservers.remove(i + 1);
+                        final String match = (String)mKeysAndObservers.remove(i);
+                        nativeRemoveMatch(match);
+                    } else {
+                        i += 2;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/UidBatteryConsumer.java b/android-34/android/os/UidBatteryConsumer.java
new file mode 100644
index 0000000..103452d
--- /dev/null
+++ b/android-34/android/os/UidBatteryConsumer.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains power consumption data attributed to a specific UID.
+ *
+ * @hide
+ */
+public final class UidBatteryConsumer extends BatteryConsumer {
+
+    static final int CONSUMER_TYPE_UID = 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STATE_FOREGROUND,
+            STATE_BACKGROUND
+    })
+    public @interface State {
+    }
+
+    /**
+     * The state of an application when it is either running a foreground (top) activity.
+     */
+    public static final int STATE_FOREGROUND = 0;
+
+    /**
+     * The state of an application when it is running in the background, including the following
+     * states:
+     *
+     * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND},
+     * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND},
+     * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP},
+     * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE},
+     * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER},
+     * {@link android.app.ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE}.
+     */
+    public static final int STATE_BACKGROUND = 1;
+
+    static final int COLUMN_INDEX_UID = BatteryConsumer.COLUMN_COUNT;
+    static final int COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN = COLUMN_INDEX_UID + 1;
+    static final int COLUMN_INDEX_TIME_IN_FOREGROUND = COLUMN_INDEX_UID + 2;
+    static final int COLUMN_INDEX_TIME_IN_BACKGROUND = COLUMN_INDEX_UID + 3;
+    static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 4;
+
+    UidBatteryConsumer(BatteryConsumerData data) {
+        super(data);
+    }
+
+    private UidBatteryConsumer(@NonNull Builder builder) {
+        super(builder.mData, builder.mPowerComponentsBuilder.build());
+    }
+
+    public int getUid() {
+        return mData.getInt(COLUMN_INDEX_UID);
+    }
+
+    @Nullable
+    public String getPackageWithHighestDrain() {
+        return mData.getString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN);
+    }
+
+    /**
+     * Returns the amount of time in milliseconds this UID spent in the specified state.
+     */
+    public long getTimeInStateMs(@State int state) {
+        switch (state) {
+            case STATE_BACKGROUND:
+                return mData.getInt(COLUMN_INDEX_TIME_IN_BACKGROUND);
+            case STATE_FOREGROUND:
+                return mData.getInt(COLUMN_INDEX_TIME_IN_FOREGROUND);
+        }
+        return 0;
+    }
+
+    @Override
+    public void dump(PrintWriter pw, boolean skipEmptyComponents) {
+        pw.print("UID ");
+        UserHandle.formatUid(pw, getUid());
+        pw.print(": ");
+        pw.print(BatteryStats.formatCharge(getConsumedPower()));
+
+        if (mData.layout.processStateDataIncluded) {
+            StringBuilder sb = new StringBuilder();
+            appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND,
+                    skipEmptyComponents);
+            appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_BACKGROUND,
+                    skipEmptyComponents);
+            appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
+                    skipEmptyComponents);
+            appendProcessStateData(sb, BatteryConsumer.PROCESS_STATE_CACHED,
+                    skipEmptyComponents);
+            pw.print(sb);
+        }
+
+        pw.print(" ( ");
+        mPowerComponents.dump(pw, skipEmptyComponents  /* skipTotalPowerComponent */);
+        pw.print(" ) ");
+    }
+
+    private void appendProcessStateData(StringBuilder sb, @ProcessState int processState,
+            boolean skipEmptyComponents) {
+        Dimensions dimensions = new Dimensions(POWER_COMPONENT_ANY, processState);
+        final double power = mPowerComponents.getConsumedPower(dimensions);
+        if (power == 0 && skipEmptyComponents) {
+            return;
+        }
+
+        sb.append(" ").append(processStateToString(processState)).append(": ")
+                .append(BatteryStats.formatCharge(power));
+    }
+
+    static UidBatteryConsumer create(BatteryConsumerData data) {
+        return new UidBatteryConsumer(data);
+    }
+
+    /** Serializes this object to XML */
+    void writeToXml(TypedXmlSerializer serializer) throws IOException {
+        if (getConsumedPower() == 0) {
+            return;
+        }
+
+        serializer.startTag(null, BatteryUsageStats.XML_TAG_UID);
+        serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid());
+        final String packageWithHighestDrain = getPackageWithHighestDrain();
+        if (!TextUtils.isEmpty(packageWithHighestDrain)) {
+            serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE,
+                    packageWithHighestDrain);
+        }
+        serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND,
+                getTimeInStateMs(STATE_FOREGROUND));
+        serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND,
+                getTimeInStateMs(STATE_BACKGROUND));
+        mPowerComponents.writeToXml(serializer);
+        serializer.endTag(null, BatteryUsageStats.XML_TAG_UID);
+    }
+
+    /** Parses an XML representation and populates the BatteryUsageStats builder */
+    static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+            throws XmlPullParserException, IOException {
+        final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID);
+        final UidBatteryConsumer.Builder consumerBuilder =
+                builder.getOrCreateUidBatteryConsumerBuilder(uid);
+
+        int eventType = parser.getEventType();
+        if (eventType != XmlPullParser.START_TAG
+                || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) {
+            throw new XmlPullParserException("Invalid XML parser state");
+        }
+
+        consumerBuilder.setPackageWithHighestDrain(
+                parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE));
+        consumerBuilder.setTimeInStateMs(STATE_FOREGROUND,
+                parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND));
+        consumerBuilder.setTimeInStateMs(STATE_BACKGROUND,
+                parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND));
+        while (!(eventType == XmlPullParser.END_TAG
+                && parser.getName().equals(BatteryUsageStats.XML_TAG_UID))
+                && eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+                    PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+                }
+            }
+            eventType = parser.next();
+        }
+    }
+
+    /**
+     * Builder for UidBatteryConsumer.
+     */
+    public static final class Builder extends BaseBuilder<Builder> {
+        private static final String PACKAGE_NAME_UNINITIALIZED = "";
+        private final BatteryStats.Uid mBatteryStatsUid;
+        private final int mUid;
+        private final boolean mIsVirtualUid;
+        private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED;
+        private boolean mExcludeFromBatteryUsageStats;
+
+        public Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid) {
+            this(data, batteryStatsUid, batteryStatsUid.getUid());
+        }
+
+        public Builder(BatteryConsumerData data, int uid) {
+            this(data, null, uid);
+        }
+
+        private Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid,
+                int uid) {
+            super(data, CONSUMER_TYPE_UID);
+            mBatteryStatsUid = batteryStatsUid;
+            mUid = uid;
+            mIsVirtualUid = mUid == Process.SDK_SANDBOX_VIRTUAL_UID;
+            data.putLong(COLUMN_INDEX_UID, mUid);
+        }
+
+        @NonNull
+        public BatteryStats.Uid getBatteryStatsUid() {
+            if (mBatteryStatsUid == null) {
+                throw new IllegalStateException(
+                        "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid");
+            }
+            return mBatteryStatsUid;
+        }
+
+        public int getUid() {
+            return mUid;
+        }
+
+        public boolean isVirtualUid() {
+            return mIsVirtualUid;
+        }
+
+        /**
+         * Sets the name of the package owned by this UID that consumed the highest amount
+         * of power since BatteryStats reset.
+         */
+        @NonNull
+        public Builder setPackageWithHighestDrain(@Nullable String packageName) {
+            mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName);
+            return this;
+        }
+
+        /**
+         * Sets the duration, in milliseconds, that this UID was active in a particular state,
+         * such as foreground or background.
+         */
+        @NonNull
+        public Builder setTimeInStateMs(@State int state, long timeInStateMs) {
+            switch (state) {
+                case STATE_FOREGROUND:
+                    mData.putLong(COLUMN_INDEX_TIME_IN_FOREGROUND, timeInStateMs);
+                    break;
+                case STATE_BACKGROUND:
+                    mData.putLong(COLUMN_INDEX_TIME_IN_BACKGROUND, timeInStateMs);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported state: " + state);
+            }
+            return this;
+        }
+
+        /**
+         * Marks the UidBatteryConsumer for exclusion from the result set.
+         */
+        public Builder excludeFromBatteryUsageStats() {
+            mExcludeFromBatteryUsageStats = true;
+            return this;
+        }
+
+        /**
+         * Adds power and usage duration from the supplied UidBatteryConsumer.
+         */
+        public Builder add(UidBatteryConsumer consumer) {
+            mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents);
+
+            setTimeInStateMs(STATE_FOREGROUND,
+                    mData.getLong(COLUMN_INDEX_TIME_IN_FOREGROUND)
+                            + consumer.getTimeInStateMs(STATE_FOREGROUND));
+            setTimeInStateMs(STATE_BACKGROUND,
+                    mData.getLong(COLUMN_INDEX_TIME_IN_BACKGROUND)
+                            + consumer.getTimeInStateMs(STATE_BACKGROUND));
+
+            if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+                mPackageWithHighestDrain = consumer.getPackageWithHighestDrain();
+            } else if (!TextUtils.equals(mPackageWithHighestDrain,
+                    consumer.getPackageWithHighestDrain())) {
+                // Consider combining two UidBatteryConsumers with this distribution
+                // of power drain between packages:
+                // (package1=100, package2=10) and (package1=100, package2=101).
+                // Since we don't know the actual power distribution between packages at this
+                // point, we have no way to correctly declare package1 as the winner.
+                // The naive logic of picking the consumer with the higher total consumed
+                // power would produce an incorrect result.
+                mPackageWithHighestDrain = null;
+            }
+            return this;
+        }
+
+        /**
+         * Returns true if this UidBatteryConsumer must be excluded from the
+         * BatteryUsageStats.
+         */
+        public boolean isExcludedFromBatteryUsageStats() {
+            return mExcludeFromBatteryUsageStats;
+        }
+
+        /**
+         * Creates a read-only object out of the Builder values.
+         */
+        @NonNull
+        public UidBatteryConsumer build() {
+            if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+                mPackageWithHighestDrain = null;
+            }
+            if (mPackageWithHighestDrain != null) {
+                mData.putString(COLUMN_INDEX_PACKAGE_WITH_HIGHEST_DRAIN, mPackageWithHighestDrain);
+            }
+            return new UidBatteryConsumer(this);
+        }
+    }
+}
diff --git a/android-34/android/os/UpdateEngine.java b/android-34/android/os/UpdateEngine.java
new file mode 100644
index 0000000..b7e3068
--- /dev/null
+++ b/android-34/android/os/UpdateEngine.java
@@ -0,0 +1,666 @@
+/*
+ * 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.content.res.AssetFileDescriptor;
+import android.os.IUpdateEngine;
+import android.os.IUpdateEngineCallback;
+import android.os.RemoteException;
+
+/**
+ * UpdateEngine handles calls to the update engine which takes care of A/B OTA
+ * updates. It wraps up the update engine Binder APIs and exposes them as
+ * SystemApis, which will be called by the system app responsible for OTAs.
+ * On a Google device, this will be GmsCore.
+ *
+ * The minimal flow is:
+ * <ol>
+ * <li>Create a new UpdateEngine instance.
+ * <li>Call {@link #bind}, optionally providing callbacks.
+ * <li>Call {@link #applyPayload}.
+ * </ol>
+ *
+ * In addition, methods are provided to {@link #cancel} or
+ * {@link #suspend}/{@link #resume} application of an update.
+ *
+ * The APIs defined in this class and UpdateEngineCallback class must be in
+ * sync with the ones in
+ * {@code system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl}
+ * and
+ * {@code system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl}.
+ *
+ * {@hide}
+ */
+@SystemApi
+public class UpdateEngine {
+    private static final String TAG = "UpdateEngine";
+
+    private static final String UPDATE_ENGINE_SERVICE = "android.os.UpdateEngineService";
+
+    /**
+     * Error codes from update engine upon finishing a call to
+     * {@link applyPayload}. Values will be passed via the callback function
+     * {@link UpdateEngineCallback#onPayloadApplicationComplete}. Values must
+     * agree with the ones in {@code system/update_engine/common/error_code.h}.
+     */
+    public static final class ErrorCodeConstants {
+        /**
+         * Error code: a request finished successfully.
+         */
+        public static final int SUCCESS = 0;
+        /**
+         * Error code: a request failed due to a generic error.
+         */
+        public static final int ERROR = 1;
+        /**
+         * Error code: an update failed to apply due to filesystem copier
+         * error.
+         */
+        public static final int FILESYSTEM_COPIER_ERROR = 4;
+        /**
+         * Error code: an update failed to apply due to an error in running
+         * post-install hooks.
+         */
+        public static final int POST_INSTALL_RUNNER_ERROR = 5;
+        /**
+         * Error code: an update failed to apply due to a mismatching payload.
+         *
+         * <p>For example, the given payload uses a feature that's not
+         * supported by the current update engine.
+         */
+        public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6;
+        /**
+         * Error code: an update failed to apply due to an error in opening
+         * devices.
+         */
+        public static final int INSTALL_DEVICE_OPEN_ERROR = 7;
+        /**
+         * Error code: an update failed to apply due to an error in opening
+         * kernel device.
+         */
+        public static final int KERNEL_DEVICE_OPEN_ERROR = 8;
+        /**
+         * Error code: an update failed to apply due to an error in fetching
+         * the payload.
+         *
+         * <p>For example, this could be a result of bad network connection
+         * when streaming an update.
+         */
+        public static final int DOWNLOAD_TRANSFER_ERROR = 9;
+        /**
+         * Error code: an update failed to apply due to a mismatch in payload
+         * hash.
+         *
+         * <p>Update engine does validity checks for the given payload and its
+         * metadata.
+         */
+        public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
+
+        /**
+         * Error code: an update failed to apply due to a mismatch in payload
+         * size.
+         */
+        public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
+
+        /**
+         * Error code: an update failed to apply due to failing to verify
+         * payload signatures.
+         */
+        public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
+
+        /**
+         * Error code: an update failed to apply due to a downgrade in payload
+         * timestamp.
+         *
+         * <p>The timestamp of a build is encoded into the payload, which will
+         * be enforced during install to prevent downgrading a device.
+         */
+        public static final int PAYLOAD_TIMESTAMP_ERROR = 51;
+
+        /**
+         * Error code: an update has been applied successfully but the new slot
+         * hasn't been set to active.
+         *
+         * <p>It indicates a successful finish of calling {@link #applyPayload} with
+         * {@code SWITCH_SLOT_ON_REBOOT=0}. See {@link #applyPayload}.
+         */
+        public static final int UPDATED_BUT_NOT_ACTIVE = 52;
+
+        /**
+         * Error code: there is not enough space on the device to apply the update. User should
+         * be prompted to free up space and re-try the update.
+         *
+         * <p>See {@link UpdateEngine#allocateSpace}.
+         */
+        public static final int NOT_ENOUGH_SPACE = 60;
+
+        /**
+         * Error code: the device is corrupted and no further updates may be applied.
+         *
+         * <p>See {@link UpdateEngine#cleanupAppliedPayload}.
+         */
+        public static final int DEVICE_CORRUPTED = 61;
+    }
+
+    /** @hide */
+    @IntDef(value = {
+            ErrorCodeConstants.SUCCESS,
+            ErrorCodeConstants.ERROR,
+            ErrorCodeConstants.FILESYSTEM_COPIER_ERROR,
+            ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+            ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+            ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+            ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR,
+            ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+            ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+            ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+            ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+            ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+            ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+            ErrorCodeConstants.NOT_ENOUGH_SPACE,
+            ErrorCodeConstants.DEVICE_CORRUPTED,
+    })
+    public @interface ErrorCode {}
+
+    /**
+     * Status codes for update engine. Values must agree with the ones in
+     * {@code system/update_engine/client_library/include/update_engine/update_status.h}.
+     */
+    public static final class UpdateStatusConstants {
+        /**
+         * Update status code: update engine is in idle state.
+         */
+        public static final int IDLE = 0;
+
+        /**
+         * Update status code: update engine is checking for update.
+         */
+        public static final int CHECKING_FOR_UPDATE = 1;
+
+        /**
+         * Update status code: an update is available.
+         */
+        public static final int UPDATE_AVAILABLE = 2;
+
+        /**
+         * Update status code: update engine is downloading an update.
+         */
+        public static final int DOWNLOADING = 3;
+
+        /**
+         * Update status code: update engine is verifying an update.
+         */
+        public static final int VERIFYING = 4;
+
+        /**
+         * Update status code: update engine is finalizing an update.
+         */
+        public static final int FINALIZING = 5;
+
+        /**
+         * Update status code: an update has been applied and is pending for
+         * reboot.
+         */
+        public static final int UPDATED_NEED_REBOOT = 6;
+
+        /**
+         * Update status code: update engine is reporting an error event.
+         */
+        public static final int REPORTING_ERROR_EVENT = 7;
+
+        /**
+         * Update status code: update engine is attempting to rollback an
+         * update.
+         */
+        public static final int ATTEMPTING_ROLLBACK = 8;
+
+        /**
+         * Update status code: update engine is in disabled state.
+         */
+        public static final int DISABLED = 9;
+    }
+
+    private final IUpdateEngine mUpdateEngine;
+    private IUpdateEngineCallback mUpdateEngineCallback = null;
+    private final Object mUpdateEngineCallbackLock = new Object();
+
+    /**
+     * Creates a new instance.
+     */
+    public UpdateEngine() {
+        mUpdateEngine = IUpdateEngine.Stub.asInterface(
+                ServiceManager.getService(UPDATE_ENGINE_SERVICE));
+        if (mUpdateEngine == null) {
+            throw new IllegalStateException("Failed to find update_engine");
+        }
+    }
+
+    /**
+     * Prepares this instance for use. The callback will be notified on any
+     * status change, and when the update completes. A handler can be supplied
+     * to control which thread runs the callback, or null.
+     */
+    public boolean bind(final UpdateEngineCallback callback, final Handler handler) {
+        synchronized (mUpdateEngineCallbackLock) {
+            mUpdateEngineCallback = new IUpdateEngineCallback.Stub() {
+                @Override
+                public void onStatusUpdate(final int status, final float percent) {
+                    if (handler != null) {
+                        handler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                callback.onStatusUpdate(status, percent);
+                            }
+                        });
+                    } else {
+                        callback.onStatusUpdate(status, percent);
+                    }
+                }
+
+                @Override
+                public void onPayloadApplicationComplete(final int errorCode) {
+                    if (handler != null) {
+                        handler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                callback.onPayloadApplicationComplete(errorCode);
+                            }
+                        });
+                    } else {
+                        callback.onPayloadApplicationComplete(errorCode);
+                    }
+                }
+            };
+
+            try {
+                return mUpdateEngine.bind(mUpdateEngineCallback);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Equivalent to {@code bind(callback, null)}.
+     */
+    public boolean bind(final UpdateEngineCallback callback) {
+        return bind(callback, null);
+    }
+
+    /**
+     * Applies the payload found at the given {@code url}. For non-streaming
+     * updates, the URL can be a local file using the {@code file://} scheme.
+     *
+     * <p>The {@code offset} and {@code size} parameters specify the location
+     * of the payload within the file represented by the URL. This is useful
+     * if the downloadable package at the URL contains more than just the
+     * update_engine payload (such as extra metadata). This is true for
+     * Google's OTA system, where the URL points to a zip file in which the
+     * payload is stored uncompressed within the zip file alongside other
+     * data.
+     *
+     * <p>The {@code headerKeyValuePairs} parameter is used to pass metadata
+     * to update_engine. In Google's implementation, this is stored as
+     * {@code payload_properties.txt} in the zip file. It's generated by the
+     * script {@code system/update_engine/scripts/brillo_update_payload}.
+     * The complete list of keys and their documentation is in
+     * {@code system/update_engine/common/constants.cc}, but an example
+     * might be:
+     * <pre>
+     * String[] pairs = {
+     *   "FILE_HASH=lURPCIkIAjtMOyB/EjQcl8zDzqtD6Ta3tJef6G/+z2k=",
+     *   "FILE_SIZE=871903868",
+     *   "METADATA_HASH=tBvj43QOB0Jn++JojcpVdbRLz0qdAuL+uTkSy7hokaw=",
+     *   "METADATA_SIZE=70604"
+     * };
+     * </pre>
+     *
+     * <p>The callback functions registered via {@code #bind} will be called
+     * during and at the end of the payload application.
+     *
+     * <p>By default the newly updated slot will be set active upon
+     * successfully finishing an update. Device will attempt to boot into the
+     * new slot on next reboot. This behavior can be customized by specifying
+     * {@code SWITCH_SLOT_ON_REBOOT=0} in {@code headerKeyValuePairs}, which
+     * allows the caller to later determine a good time to boot into the new
+     * slot. Calling {@code applyPayload} again with the same payload but with
+     * {@code SWITCH_SLOT_ON_REBOOT=1} will do the minimal work to set the new
+     * slot active, after verifying its integrity.
+     */
+    public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) {
+        try {
+            mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Applies the payload passed as AssetFileDescriptor {@code assetFd}
+     * instead of using the {@code file://} scheme.
+     *
+     * <p>See {@link #applyPayload(String)} for {@code offset}, {@code size} and
+     * {@code headerKeyValuePairs} parameters.
+     */
+    public void applyPayload(@NonNull AssetFileDescriptor assetFd,
+            @NonNull String[] headerKeyValuePairs) {
+        try {
+            mUpdateEngine.applyPayloadFd(assetFd.getParcelFileDescriptor(),
+                    assetFd.getStartOffset(), assetFd.getLength(), headerKeyValuePairs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Permanently cancels an in-progress update.
+     *
+     * <p>See {@link #resetStatus} to undo a finshed update (only available
+     * before the updated system has been rebooted).
+     *
+     * <p>See {@link #suspend} for a way to temporarily stop an in-progress
+     * update with the ability to resume it later.
+     */
+    public void cancel() {
+        try {
+            mUpdateEngine.cancel();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Suspends an in-progress update. This can be undone by calling
+     * {@link #resume}.
+     */
+    public void suspend() {
+        try {
+            mUpdateEngine.suspend();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resumes a suspended update.
+     */
+    public void resume() {
+        try {
+            mUpdateEngine.resume();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resets the bootable flag on the non-current partition and all internal
+     * update_engine state. Note this call will clear the entire update
+     * progress. So a subsequent {@link #applyPayload} will apply the update
+     * from scratch.
+     *
+     * <p>After this call completes, update_engine will no longer report
+     * {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding
+     * notification that rebooting into the new system is possible.
+     */
+    public void resetStatus() {
+        try {
+            mUpdateEngine.resetStatus();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the A/B slot switch for the next boot after applying an ota update. If
+     * {@link #applyPayload} hasn't switched the slot, the updater APP can call
+     * this API to switch the slot and apply the update on next boot.
+     *
+     * @param payloadMetadataFilename the location of the metadata without the
+     * {@code file://} prefix.
+     */
+    public void setShouldSwitchSlotOnReboot(@NonNull String payloadMetadataFilename) {
+        try {
+            mUpdateEngine.setShouldSwitchSlotOnReboot(payloadMetadataFilename);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+   /**
+    * Resets the boot slot to the source/current slot, without cancelling the
+    * update progress. This can be called after the update is installed, and to
+    * prevent the device from accidentally taking the update when it reboots.
+    *
+    * This is useful when users don't want to take the update immediately; or
+    * the updater determines some condition hasn't met, e.g. insufficient space
+    * for boot.
+    */
+    public void resetShouldSwitchSlotOnReboot() {
+        try {
+            mUpdateEngine.resetShouldSwitchSlotOnReboot();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unbinds the last bound callback function.
+     */
+    public boolean unbind() {
+        synchronized (mUpdateEngineCallbackLock) {
+            if (mUpdateEngineCallback == null) {
+                return true;
+            }
+            try {
+                boolean result = mUpdateEngine.unbind(mUpdateEngineCallback);
+                mUpdateEngineCallback = null;
+                return result;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Verifies that a payload associated with the given payload metadata
+     * {@code payloadMetadataFilename} can be safely applied to ths device.
+     * Returns {@code true} if the update can successfully be applied and
+     * returns {@code false} otherwise.
+     *
+     * @param payloadMetadataFilename the location of the metadata without the
+     * {@code file://} prefix.
+     */
+    public boolean verifyPayloadMetadata(String payloadMetadataFilename) {
+        try {
+            return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return value of {@link #allocateSpace.}
+     */
+    public static final class AllocateSpaceResult {
+        private @ErrorCode int mErrorCode = ErrorCodeConstants.SUCCESS;
+        private long mFreeSpaceRequired = 0;
+        private AllocateSpaceResult() {}
+        /**
+         * Error code.
+         *
+         * @return The following error codes:
+         * <ul>
+         * <li>{@link ErrorCodeConstants#SUCCESS} if space has been allocated
+         *         successfully.</li>
+         * <li>{@link ErrorCodeConstants#NOT_ENOUGH_SPACE} if insufficient
+         *         space.</li>
+         * <li>Other {@link ErrorCodeConstants} for other errors.</li>
+         * </ul>
+         */
+        @ErrorCode
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+
+        /**
+         * Estimated total space that needs to be available on the userdata partition to apply the
+         * payload (in bytes).
+         *
+         * <p>
+         * Note that in practice, more space needs to be made available before applying the payload
+         * to keep the device working.
+         *
+         * @return The following values:
+         * <ul>
+         * <li>zero if {@link #getErrorCode} returns {@link ErrorCodeConstants#SUCCESS}</li>
+         * <li>non-zero if {@link #getErrorCode} returns
+         * {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}.
+         * Value is the estimated total space required on userdata partition.</li>
+         * </ul>
+         * @throws IllegalStateException if {@link #getErrorCode} is not one of the above.
+         *
+         */
+        public long getFreeSpaceRequired() {
+            if (mErrorCode == ErrorCodeConstants.SUCCESS) {
+                return 0;
+            }
+            if (mErrorCode == ErrorCodeConstants.NOT_ENOUGH_SPACE) {
+                return mFreeSpaceRequired;
+            }
+            throw new IllegalStateException(String.format(
+                    "getFreeSpaceRequired() is not available when error code is %d", mErrorCode));
+        }
+    }
+
+    /**
+     * Initialize partitions for a payload associated with the given payload
+     * metadata {@code payloadMetadataFilename} by preallocating required space.
+     *
+     * <p>This function should be called after payload has been verified after
+     * {@link #verifyPayloadMetadata}. This function does not verify whether
+     * the given payload is applicable or not.
+     *
+     * <p>Implementation of {@code allocateSpace} uses
+     * {@code headerKeyValuePairs} to determine whether space has been allocated
+     * for a different or same payload previously. If space has been allocated
+     * for a different payload before, space will be reallocated for the given
+     * payload. If space has been allocated for the same payload, no actions to
+     * storage devices are taken.
+     *
+     * <p>This function is synchronous and may take a non-trivial amount of
+     * time. Callers should call this function in a background thread.
+     *
+     * @param payloadMetadataFilename See {@link #verifyPayloadMetadata}.
+     * @param headerKeyValuePairs See {@link #applyPayload}.
+     * @return See {@link AllocateSpaceResult#getErrorCode} and
+     *             {@link AllocateSpaceResult#getFreeSpaceRequired}.
+     */
+    @WorkerThread
+    @NonNull
+    public AllocateSpaceResult allocateSpace(
+                @NonNull String payloadMetadataFilename,
+                @NonNull String[] headerKeyValuePairs) {
+        AllocateSpaceResult result = new AllocateSpaceResult();
+        try {
+            result.mFreeSpaceRequired = mUpdateEngine.allocateSpaceForPayload(
+                    payloadMetadataFilename,
+                    headerKeyValuePairs);
+            result.mErrorCode = result.mFreeSpaceRequired == 0
+                    ? ErrorCodeConstants.SUCCESS
+                    : ErrorCodeConstants.NOT_ENOUGH_SPACE;
+            return result;
+        } catch (ServiceSpecificException e) {
+            result.mErrorCode = e.errorCode;
+            result.mFreeSpaceRequired = 0;
+            return result;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static class CleanupAppliedPayloadCallback extends IUpdateEngineCallback.Stub {
+        private int mErrorCode = ErrorCodeConstants.ERROR;
+        private boolean mCompleted = false;
+        private Object mLock = new Object();
+        private int getResult() {
+            synchronized (mLock) {
+                while (!mCompleted) {
+                    try {
+                        mLock.wait();
+                    } catch (InterruptedException ex) {
+                        // do nothing, just wait again.
+                    }
+                }
+                return mErrorCode;
+            }
+        }
+
+        @Override
+        public void onStatusUpdate(int status, float percent) {
+        }
+
+        @Override
+        public void onPayloadApplicationComplete(int errorCode) {
+            synchronized (mLock) {
+                mErrorCode = errorCode;
+                mCompleted = true;
+                mLock.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Cleanup files used by the previous update and free up space after the
+     * device has been booted successfully into the new build.
+     *
+     * <p>In particular, this function waits until delta files for snapshots for
+     * Virtual A/B update are merged to OS partitions, then delete these delta
+     * files.
+     *
+     * <p>This function is synchronous and may take a non-trivial amount of
+     * time. Callers should call this function in a background thread.
+     *
+     * <p>This function does not delete payload binaries downloaded for a
+     * non-streaming OTA update.
+     *
+     * @return One of the following:
+     * <ul>
+     * <li>{@link ErrorCodeConstants#SUCCESS} if execution is successful.</li>
+     * <li>{@link ErrorCodeConstants#ERROR} if a transient error has occurred.
+     * The device should be able to recover after a reboot. The function should
+     * be retried after the reboot.</li>
+     * <li>{@link ErrorCodeConstants#DEVICE_CORRUPTED} if a permanent error is
+     * encountered. Device is corrupted, and future updates must not be applied.
+     * The device cannot recover without flashing and factory resets.
+     * </ul>
+     */
+    @WorkerThread
+    @ErrorCode
+    public int cleanupAppliedPayload() {
+        CleanupAppliedPayloadCallback callback = new CleanupAppliedPayloadCallback();
+        try {
+            mUpdateEngine.cleanupSuccessfulUpdate(callback);
+            return callback.getResult();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-34/android/os/UpdateEngineCallback.java b/android-34/android/os/UpdateEngineCallback.java
new file mode 100644
index 0000000..7fe7024
--- /dev/null
+++ b/android-34/android/os/UpdateEngineCallback.java
@@ -0,0 +1,49 @@
+/*
+ * 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.os;
+
+import android.annotation.SystemApi;
+
+/**
+ * Callback function for UpdateEngine. Used to keep the caller up to date
+ * with progress, so the UI (if any) can be updated.
+ *
+ * The APIs defined in this class and UpdateEngine class must be in sync with
+ * the ones in
+ * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
+ * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class UpdateEngineCallback {
+
+    /**
+     * Invoked when anything changes. The value of {@code status} will
+     * be one of the values from {@link UpdateEngine.UpdateStatusConstants},
+     * and {@code percent} will be valid [TODO: in which cases?].
+     */
+    public abstract void onStatusUpdate(int status, float percent);
+
+    /**
+     * Invoked when the payload has been applied, whether successfully or
+     * unsuccessfully. The value of {@code errorCode} will be one of the
+     * values from {@link UpdateEngine.ErrorCodeConstants}.
+     */
+    public abstract void onPayloadApplicationComplete(
+            @UpdateEngine.ErrorCode int errorCode);
+}
diff --git a/android-34/android/os/UpdateLock.java b/android-34/android/os/UpdateLock.java
new file mode 100644
index 0000000..5aa9401
--- /dev/null
+++ b/android-34/android/os/UpdateLock.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Advisory wakelock-like mechanism by which processes that should not be interrupted for
+ * OTA/update purposes can so advise the OS.  This is particularly relevant for headless
+ * or kiosk-like operation.
+ *
+ * @hide
+ */
+public class UpdateLock {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "UpdateLock";
+
+    private static IUpdateLock sService;
+    private static void checkService() {
+        if (sService == null) {
+            sService = IUpdateLock.Stub.asInterface(
+                    ServiceManager.getService(Context.UPDATE_LOCK_SERVICE));
+        }
+    }
+
+    IBinder mToken;
+    int mCount = 0;
+    boolean mRefCounted = true;
+    boolean mHeld = false;
+    final String mTag;
+
+    /**
+     * Broadcast Intent action sent when the global update lock state changes,
+     * i.e. when the first locker acquires an update lock, or when the last
+     * locker releases theirs.  The broadcast is sticky but is sent only to
+     * registered receivers.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED";
+
+    /**
+     * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating
+     * whether now is an appropriate time to interrupt device activity with an
+     * update operation.  True means that updates are okay right now; false indicates
+     * that perhaps later would be a better time.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final String NOW_IS_CONVENIENT = "nowisconvenient";
+
+    /**
+     * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the
+     * wall-clock time [in UTC] at which the broadcast was sent.  Note that this is
+     * in the System.currentTimeMillis() time base, which may be non-monotonic especially
+     * around reboots.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final String TIMESTAMP = "timestamp";
+
+    /**
+     * Construct an UpdateLock instance.
+     * @param tag An arbitrary string used to identify this lock instance in dump output.
+     */
+    public UpdateLock(String tag) {
+        mTag = tag;
+        mToken = new Binder();
+    }
+
+    /**
+     * Change the refcount behavior of this update lock.
+     */
+    public void setReferenceCounted(boolean isRefCounted) {
+        if (DEBUG) {
+            Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this);
+        }
+        mRefCounted = isRefCounted;
+    }
+
+    /**
+     * Is this lock currently held?
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean isHeld() {
+        synchronized (mToken) {
+            return mHeld;
+        }
+    }
+
+    /**
+     * Acquire an update lock.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void acquire() {
+        if (DEBUG) {
+            Log.v(TAG, "acquire() : " + this, new RuntimeException("here"));
+        }
+        checkService();
+        synchronized (mToken) {
+            acquireLocked();
+        }
+    }
+
+    private void acquireLocked() {
+        if (!mRefCounted || mCount++ == 0) {
+            if (sService != null) {
+                try {
+                    sService.acquireUpdateLock(mToken, mTag);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to acquire");
+                }
+            }
+            mHeld = true;
+        }
+    }
+
+    /**
+     * Release this update lock.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void release() {
+        if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here"));
+        checkService();
+        synchronized (mToken) {
+            releaseLocked();
+        }
+    }
+
+    private void releaseLocked() {
+        if (!mRefCounted || --mCount == 0) {
+            if (sService != null) {
+                try {
+                    sService.releaseUpdateLock(mToken);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to release");
+                }
+            }
+            mHeld = false;
+        }
+        if (mCount < 0) {
+            throw new RuntimeException("UpdateLock under-locked");
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        synchronized (mToken) {
+            // if mHeld is true, sService must be non-null
+            if (mHeld) {
+                Log.wtf(TAG, "UpdateLock finalized while still held");
+                try {
+                    sService.releaseUpdateLock(mToken);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to release");
+                }
+            }
+        }
+    }
+}
diff --git a/android-34/android/os/UserBatteryConsumer.java b/android-34/android/os/UserBatteryConsumer.java
new file mode 100644
index 0000000..6b4a5cf
--- /dev/null
+++ b/android-34/android/os/UserBatteryConsumer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.NonNull;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains power consumption data attributed to a {@link UserHandle}.
+ *
+ * {@hide}
+ */
+public class UserBatteryConsumer extends BatteryConsumer {
+    static final int CONSUMER_TYPE_USER = 2;
+
+    private static final int COLUMN_INDEX_USER_ID = BatteryConsumer.COLUMN_COUNT;
+
+    static final int COLUMN_COUNT = BatteryConsumer.COLUMN_COUNT + 1;
+
+    UserBatteryConsumer(BatteryConsumerData data) {
+        super(data);
+    }
+
+    private UserBatteryConsumer(@NonNull UserBatteryConsumer.Builder builder) {
+        super(builder.mData, builder.mPowerComponentsBuilder.build());
+    }
+
+    public int getUserId() {
+        return mData.getInt(COLUMN_INDEX_USER_ID);
+    }
+
+    @Override
+    public void dump(PrintWriter pw, boolean skipEmptyComponents) {
+        final double consumedPower = getConsumedPower();
+        pw.print("User ");
+        pw.print(getUserId());
+        pw.print(": ");
+        pw.print(BatteryStats.formatCharge(consumedPower));
+        pw.print(" ( ");
+        mPowerComponents.dump(pw, skipEmptyComponents  /* skipTotalPowerComponent */);
+        pw.print(" ) ");
+    }
+
+    /** Serializes this object to XML */
+    void writeToXml(TypedXmlSerializer serializer) throws IOException {
+        if (getConsumedPower() == 0) {
+            return;
+        }
+
+        serializer.startTag(null, BatteryUsageStats.XML_TAG_USER);
+        serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID, getUserId());
+        mPowerComponents.writeToXml(serializer);
+        serializer.endTag(null, BatteryUsageStats.XML_TAG_USER);
+    }
+
+    /** Parses an XML representation and populates the BatteryUsageStats builder */
+    static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+            throws XmlPullParserException, IOException {
+        final int userId = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID);
+        final UserBatteryConsumer.Builder consumerBuilder =
+                builder.getOrCreateUserBatteryConsumerBuilder(userId);
+
+        int eventType = parser.getEventType();
+        if (eventType != XmlPullParser.START_TAG
+                || !parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) {
+            throw new XmlPullParserException("Invalid XML parser state");
+        }
+        while (!(eventType == XmlPullParser.END_TAG
+                && parser.getName().equals(BatteryUsageStats.XML_TAG_USER))
+                && eventType != XmlPullParser.END_DOCUMENT) {
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+                    PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+                }
+            }
+            eventType = parser.next();
+        }
+    }
+
+    /**
+     * Builder for UserBatteryConsumer.
+     */
+    public static final class Builder extends BaseBuilder<Builder> {
+        private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
+
+        Builder(BatteryConsumerData data, int userId) {
+            super(data, CONSUMER_TYPE_USER);
+            data.putLong(COLUMN_INDEX_USER_ID, userId);
+        }
+
+        /**
+         * Add a UidBatteryConsumer to this UserBatteryConsumer.
+         * <p>
+         * Calculated power and duration components of the added UID battery consumers
+         * are aggregated at the time the UserBatteryConsumer is built by the {@link #build()}
+         * method.
+         * </p>
+         */
+        public void addUidBatteryConsumer(UidBatteryConsumer.Builder uidBatteryConsumerBuilder) {
+            if (mUidBatteryConsumers == null) {
+                mUidBatteryConsumers = new ArrayList<>();
+            }
+            mUidBatteryConsumers.add(uidBatteryConsumerBuilder);
+        }
+
+        /**
+         * Creates a read-only object out of the Builder values.
+         */
+        @NonNull
+        public UserBatteryConsumer build() {
+            if (mUidBatteryConsumers != null) {
+                for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
+                    UidBatteryConsumer.Builder uidBatteryConsumer = mUidBatteryConsumers.get(i);
+                    mPowerComponentsBuilder.addPowerAndDuration(
+                            uidBatteryConsumer.mPowerComponentsBuilder);
+                }
+            }
+            return new UserBatteryConsumer(this);
+        }
+    }
+}
diff --git a/android-34/android/os/UserHandle.java b/android-34/android/os/UserHandle.java
new file mode 100644
index 0000000..ef39010
--- /dev/null
+++ b/android-34/android/os/UserHandle.java
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2011 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.os;
+
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Representation of a user on the device.
+ */
+public final class UserHandle implements Parcelable {
+    // NOTE: keep logic in sync with system/core/libcutils/multiuser.c
+
+    /**
+     * @hide Range of uids allocated for a user.
+     */
+    @UnsupportedAppUsage
+    public static final int PER_USER_RANGE = 100000;
+
+    /** @hide A user id to indicate all users on the device */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final @UserIdInt int USER_ALL = -1;
+
+    /** @hide A user handle to indicate all users on the device */
+    @SystemApi
+    public static final @NonNull UserHandle ALL = new UserHandle(USER_ALL);
+
+    /** @hide A user id to indicate the currently active user */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final @UserIdInt int USER_CURRENT = -2;
+
+    /** @hide A user handle to indicate the current user of the device */
+    @SystemApi
+    public static final @NonNull UserHandle CURRENT = new UserHandle(USER_CURRENT);
+
+    /** @hide A user id to indicate that we would like to send to the current
+     *  user, but if this is calling from a user process then we will send it
+     *  to the caller's user instead of failing with a security exception */
+    @UnsupportedAppUsage
+    public static final @UserIdInt int USER_CURRENT_OR_SELF = -3;
+
+    /** @hide A user handle to indicate that we would like to send to the current
+     *  user, but if this is calling from a user process then we will send it
+     *  to the caller's user instead of failing with a security exception */
+    @UnsupportedAppUsage
+    public static final @NonNull UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF);
+
+    /** @hide An undefined user id */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final @UserIdInt int USER_NULL = -10000;
+
+    private static final @NonNull UserHandle NULL = new UserHandle(USER_NULL);
+
+    /**
+     * @hide A user id constant to indicate the "owner" user of the device
+     * @deprecated Consider using either {@link UserHandle#USER_SYSTEM} constant or
+     * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public static final @UserIdInt int USER_OWNER = 0;
+
+    /**
+     * @hide A user handle to indicate the primary/owner user of the device
+     * @deprecated Consider using either {@link UserHandle#SYSTEM} constant or
+     * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public static final @NonNull UserHandle OWNER = new UserHandle(USER_OWNER);
+
+    /** @hide A user id constant to indicate the "system" user of the device */
+    @UnsupportedAppUsage
+    @TestApi
+    public static final @UserIdInt int USER_SYSTEM = 0;
+
+    /** @hide A user serial constant to indicate the "system" user of the device */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int USER_SERIAL_SYSTEM = 0;
+
+    /** @hide A user handle to indicate the "system" user of the device */
+    @SystemApi
+    public static final @NonNull UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
+
+    /**
+     * @hide Enable multi-user related side effects. Set this to false if
+     * there are problems with single user use-cases.
+     */
+    @UnsupportedAppUsage
+    public static final boolean MU_ENABLED = true;
+
+    /** @hide */
+    @TestApi
+    public static final int MIN_SECONDARY_USER_ID = 10;
+
+    /** @hide */
+    public static final int MAX_SECONDARY_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+
+    /**
+     * (Arbitrary) user handle cache size.
+     * {@link #CACHED_USER_HANDLES} caches user handles in the range of
+     * [{@link #MIN_SECONDARY_USER_ID}, {@link #MIN_SECONDARY_USER_ID} + {@link #NUM_CACHED_USERS}).
+     *
+     * For other users, we cache UserHandles in {link #sExtraUserHandleCache}.
+     *
+     * Normally, {@link #CACHED_USER_HANDLES} should cover all existing users, but use
+     * {link #sExtraUserHandleCache} to ensure {@link UserHandle#of} will not cause too many
+     * object allocations even if the device happens to have a secondary user with a large number
+     * (e.g. the user kept creating and removing the guest user?).
+     */
+    private static final int NUM_CACHED_USERS = MU_ENABLED ? 8 : 0;
+
+    /** @see #NUM_CACHED_USERS} */
+    private static final UserHandle[] CACHED_USER_HANDLES = new UserHandle[NUM_CACHED_USERS];
+
+    /**
+     * Extra cache for users beyond CACHED_USER_HANDLES.
+     *
+     * @see #NUM_CACHED_USERS
+     * @hide
+     */
+    @GuardedBy("sExtraUserHandleCache")
+    @VisibleForTesting
+    public static final SparseArray<UserHandle> sExtraUserHandleCache = new SparseArray<>(0);
+
+    /**
+     * Max size of {@link #sExtraUserHandleCache}. Once it reaches this size, we select
+     * an element to remove at random.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static final int MAX_EXTRA_USER_HANDLE_CACHE_SIZE = 32;
+
+    static {
+        // Not lazily initializing the cache, so that we can share them across processes.
+        // (We'll create them in zygote.)
+        for (int i = 0; i < CACHED_USER_HANDLES.length; i++) {
+            CACHED_USER_HANDLES[i] = new UserHandle(MIN_SECONDARY_USER_ID + i);
+        }
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int ERR_GID = -1;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int AID_ROOT = android.os.Process.ROOT_UID;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int AID_APP_START = android.os.Process.FIRST_APPLICATION_UID;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int AID_APP_END = android.os.Process.LAST_APPLICATION_UID;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int AID_SHARED_GID_START = android.os.Process.FIRST_SHARED_APPLICATION_GID;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int AID_CACHE_GID_START = android.os.Process.FIRST_APPLICATION_CACHE_GID;
+
+    /** The userId represented by this UserHandle. */
+    @UnsupportedAppUsage
+    final @UserIdInt int mHandle;
+
+    /**
+     * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
+     * user.
+     * @hide
+     */
+    public static boolean isSameUser(int uid1, int uid2) {
+        return getUserId(uid1) == getUserId(uid2);
+    }
+
+    /**
+     * Checks to see if both uids are referring to the same app id, ignoring the user id part of the
+     * uids.
+     * @param uid1 uid to compare
+     * @param uid2 other uid to compare
+     * @return whether the appId is the same for both uids
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean isSameApp(int uid1, int uid2) {
+        return getAppId(uid1) == getAppId(uid2);
+    }
+
+    /**
+     * Whether a UID is an "isolated" UID.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean isIsolated(int uid) {
+        if (uid > 0) {
+            return Process.isIsolated(uid);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Whether a UID belongs to a regular app. *Note* "Not a regular app" does not mean
+     * "it's system", because of isolated UIDs. Use {@link #isCore} for that.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static boolean isApp(int uid) {
+        if (uid > 0) {
+            final int appId = getAppId(uid);
+            return appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Whether a UID belongs to a system core component or not.
+     * @hide
+     */
+    public static boolean isCore(int uid) {
+        if (uid >= 0) {
+            final int appId = getAppId(uid);
+            return appId < Process.FIRST_APPLICATION_UID;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Whether a UID belongs to a shared app gid.
+     * @hide
+     */
+    public static boolean isSharedAppGid(int uid) {
+        return getAppIdFromSharedAppGid(uid) != -1;
+    }
+
+    /**
+     * Returns the user for a given uid.
+     * @param uid A uid for an application running in a particular user.
+     * @return A {@link UserHandle} for that user.
+     */
+    public static UserHandle getUserHandleForUid(int uid) {
+        return of(getUserId(uid));
+    }
+
+    /**
+     * Returns the user id for a given uid.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static @UserIdInt int getUserId(int uid) {
+        if (MU_ENABLED) {
+            return uid / PER_USER_RANGE;
+        } else {
+            return UserHandle.USER_SYSTEM;
+        }
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static @UserIdInt int getCallingUserId() {
+        return getUserId(Binder.getCallingUid());
+    }
+
+    /** @hide */
+    public static @AppIdInt int getCallingAppId() {
+        return getAppId(Binder.getCallingUid());
+    }
+
+    /** @hide */
+    @NonNull
+    public static int[] fromUserHandles(@NonNull List<UserHandle> users) {
+        int[] userIds = new int[users.size()];
+        for (int i = 0; i < userIds.length; ++i) {
+            userIds[i] = users.get(i).getIdentifier();
+        }
+        return userIds;
+    }
+
+    /** @hide */
+    @NonNull
+    public static List<UserHandle> toUserHandles(@NonNull int[] userIds) {
+        List<UserHandle> users = new ArrayList<>(userIds.length);
+        for (int i = 0; i < userIds.length; ++i) {
+            users.add(UserHandle.of(userIds[i]));
+        }
+        return users;
+    }
+
+    /** @hide */
+    @SystemApi
+    public static UserHandle of(@UserIdInt int userId) {
+        if (userId == USER_SYSTEM) {
+            return SYSTEM; // Most common.
+        }
+        // These are sequential; so use a switch. Maybe they'll be optimized to a table lookup.
+        switch (userId) {
+            case USER_ALL:
+                return ALL;
+
+            case USER_CURRENT:
+                return CURRENT;
+
+            case USER_CURRENT_OR_SELF:
+                return CURRENT_OR_SELF;
+        }
+        if (userId >= MIN_SECONDARY_USER_ID
+                && userId < (MIN_SECONDARY_USER_ID + CACHED_USER_HANDLES.length)) {
+            return CACHED_USER_HANDLES[userId - MIN_SECONDARY_USER_ID];
+        }
+        if (userId == USER_NULL) { // Not common.
+            return NULL;
+        }
+        return getUserHandleFromExtraCache(userId);
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public static UserHandle getUserHandleFromExtraCache(@UserIdInt int userId) {
+        synchronized (sExtraUserHandleCache) {
+            final UserHandle extraCached = sExtraUserHandleCache.get(userId);
+            if (extraCached != null) {
+                return extraCached;
+            }
+            if (sExtraUserHandleCache.size() >= MAX_EXTRA_USER_HANDLE_CACHE_SIZE) {
+                sExtraUserHandleCache.removeAt(
+                        (new Random()).nextInt(MAX_EXTRA_USER_HANDLE_CACHE_SIZE));
+            }
+            final UserHandle newHandle = new UserHandle(userId);
+            sExtraUserHandleCache.put(userId, newHandle);
+            return newHandle;
+        }
+    }
+
+    /**
+     * Returns the uid that is composed from the userId and the appId.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
+        if (MU_ENABLED && appId >= 0) {
+            return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
+        } else {
+            return appId;
+        }
+    }
+
+    /**
+     * Returns the uid representing the given appId for this UserHandle.
+     *
+     * @param appId the AppId to compose the uid
+     * @return the uid representing the given appId for this UserHandle
+     * @hide
+     */
+    @SystemApi
+    public int getUid(@AppIdInt int appId) {
+        return getUid(getIdentifier(), appId);
+    }
+
+    /**
+     * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
+     * @hide
+     */
+    @SystemApi
+    public static @AppIdInt int getAppId(int uid) {
+        return uid % PER_USER_RANGE;
+    }
+
+    /**
+     * Returns the gid shared between all apps with this userId.
+     * @hide
+     */
+    public static int getUserGid(@UserIdInt int userId) {
+        return getUid(userId, Process.SHARED_USER_GID);
+    }
+
+    /**
+     * Returns the gid shared between all users with the app that this uid represents, or -1 if the
+     * uid is invalid.
+     * @hide
+     */
+    @SystemApi
+    public static int getSharedAppGid(int uid) {
+        return getSharedAppGid(getUserId(uid), getAppId(uid));
+    }
+
+    /** @hide */
+    public static int getSharedAppGid(@UserIdInt int userId, @AppIdInt int appId) {
+        if (appId >= AID_APP_START && appId <= AID_APP_END) {
+            return (appId - AID_APP_START) + AID_SHARED_GID_START;
+        } else if (appId >= AID_ROOT && appId <= AID_APP_START) {
+            return appId;
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the app id for a given shared app gid. Returns -1 if the ID is invalid.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static @AppIdInt int getAppIdFromSharedAppGid(int gid) {
+        final int appId = getAppId(gid) + Process.FIRST_APPLICATION_UID
+                - Process.FIRST_SHARED_APPLICATION_GID;
+        if (appId < 0 || appId >= Process.FIRST_SHARED_APPLICATION_GID) {
+            return -1;
+        }
+        return appId;
+    }
+
+    /** @hide */
+    public static int getCacheAppGid(int uid) {
+        return getCacheAppGid(getUserId(uid), getAppId(uid));
+    }
+
+    /** @hide */
+    public static int getCacheAppGid(@UserIdInt int userId, @AppIdInt int appId) {
+        if (appId >= AID_APP_START && appId <= AID_APP_END) {
+            return getUid(userId, (appId - AID_APP_START) + AID_CACHE_GID_START);
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Generate a text representation of the uid, breaking out its individual
+     * components -- user, app, isolated, etc.
+     * @hide
+     */
+    public static void formatUid(StringBuilder sb, int uid) {
+        if (uid < Process.FIRST_APPLICATION_UID) {
+            sb.append(uid);
+        } else {
+            sb.append('u');
+            sb.append(getUserId(uid));
+            final int appId = getAppId(uid);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    sb.append('i');
+                    sb.append(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    sb.append("ai");
+                    sb.append(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
+            } else if (appId >= Process.FIRST_APPLICATION_UID) {
+                sb.append('a');
+                sb.append(appId - Process.FIRST_APPLICATION_UID);
+            } else {
+                sb.append('s');
+                sb.append(appId);
+            }
+        }
+    }
+
+    /**
+     * Generate a text representation of the uid, breaking out its individual
+     * components -- user, app, isolated, etc.
+     *
+     * @param uid The uid to format
+     * @return A string representing the UID with its individual components broken out
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public static String formatUid(int uid) {
+        StringBuilder sb = new StringBuilder();
+        formatUid(sb, uid);
+        return sb.toString();
+    }
+
+    /**
+     * Generate a text representation of the uid, breaking out its individual
+     * components -- user, app, isolated, etc.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static void formatUid(PrintWriter pw, int uid) {
+        if (uid < Process.FIRST_APPLICATION_UID) {
+            pw.print(uid);
+        } else {
+            pw.print('u');
+            pw.print(getUserId(uid));
+            final int appId = getAppId(uid);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    pw.print('i');
+                    pw.print(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    pw.print("ai");
+                    pw.print(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
+            } else if (appId >= Process.FIRST_APPLICATION_UID) {
+                pw.print('a');
+                pw.print(appId - Process.FIRST_APPLICATION_UID);
+            } else {
+                pw.print('s');
+                pw.print(appId);
+            }
+        }
+    }
+
+    /** @hide */
+    public static @UserIdInt int parseUserArg(String arg) {
+        int userId;
+        if ("all".equals(arg)) {
+            userId = UserHandle.USER_ALL;
+        } else if ("current".equals(arg) || "cur".equals(arg)) {
+            userId = UserHandle.USER_CURRENT;
+        } else {
+            try {
+                userId = Integer.parseInt(arg);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException("Bad user number: " + arg);
+            }
+        }
+        return userId;
+    }
+
+    /**
+     * Returns the user id of the current process
+     * @return user id of the current process
+     * @hide
+     */
+    @SystemApi
+    public static @UserIdInt int myUserId() {
+        return getUserId(Process.myUid());
+    }
+
+    /**
+     * Returns true if this UserHandle refers to the owner user; false otherwise.
+     * @return true if this UserHandle refers to the owner user; false otherwise.
+     * @hide
+     * @deprecated please use {@link #isSystem()} or check for
+     * {@link android.content.pm.UserInfo#isPrimary()}
+     * {@link android.content.pm.UserInfo#isAdmin()} based on your particular use case.
+     */
+    @Deprecated
+    @SystemApi
+    public boolean isOwner() {
+        return this.equals(OWNER);
+    }
+
+    /**
+     * @return true if this UserHandle refers to the system user; false otherwise.
+     * @hide
+     */
+    @SystemApi
+    public boolean isSystem() {
+        return this.equals(SYSTEM);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public UserHandle(@UserIdInt int userId) {
+        mHandle = userId;
+    }
+
+    /**
+     * Returns the userId stored in this UserHandle.
+     * @hide
+     */
+    @SystemApi
+    public @UserIdInt int getIdentifier() {
+        return mHandle;
+    }
+
+    @Override
+    public String toString() {
+        return "UserHandle{" + mHandle + "}";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        try {
+            if (obj != null) {
+                UserHandle other = (UserHandle)obj;
+                return mHandle == other.mHandle;
+            }
+        } catch (ClassCastException ignore) {
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mHandle;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mHandle);
+    }
+
+    /**
+     * Write a UserHandle to a Parcel, handling null pointers.  Must be
+     * read with {@link #readFromParcel(Parcel)}.
+     *
+     * @param h The UserHandle to be written.
+     * @param out The Parcel in which the UserHandle will be placed.
+     *
+     * @see #readFromParcel(Parcel)
+     */
+    public static void writeToParcel(UserHandle h, Parcel out) {
+        if (h != null) {
+            h.writeToParcel(out, 0);
+        } else {
+            out.writeInt(USER_NULL);
+        }
+    }
+
+    /**
+     * Read a UserHandle from a Parcel that was previously written
+     * with {@link #writeToParcel(UserHandle, Parcel)}, returning either
+     * a null or new object as appropriate.
+     *
+     * @param in The Parcel from which to read the UserHandle
+     * @return Returns a new UserHandle matching the previously written
+     * object, or null if a null had been written.
+     *
+     * @see #writeToParcel(UserHandle, Parcel)
+     */
+    public static UserHandle readFromParcel(Parcel in) {
+        int h = in.readInt();
+        return h != USER_NULL ? new UserHandle(h) : null;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<UserHandle> CREATOR
+            = new Parcelable.Creator<UserHandle>() {
+        public UserHandle createFromParcel(Parcel in) {
+            // Try to avoid allocation; use of() here. Keep this and the constructor below
+            // in sync.
+            return UserHandle.of(in.readInt());
+        }
+
+        public UserHandle[] newArray(int size) {
+            return new UserHandle[size];
+        }
+    };
+
+    /**
+     * Instantiate a new UserHandle from the data in a Parcel that was
+     * previously written with {@link #writeToParcel(Parcel, int)}.  Note that you
+     * must not use this with data written by
+     * {@link #writeToParcel(UserHandle, Parcel)} since it is not possible
+     * to handle a null UserHandle here.
+     *
+     * @param in The Parcel containing the previously written UserHandle,
+     * positioned at the location in the buffer where it was written.
+     */
+    public UserHandle(Parcel in) {
+        mHandle = in.readInt(); // Keep this and createFromParcel() in sync.
+    }
+}
diff --git a/android-34/android/os/UserManager.java b/android-34/android/os/UserManager.java
new file mode 100644
index 0000000..7d68b44
--- /dev/null
+++ b/android-34/android/os/UserManager.java
@@ -0,0 +1,6136 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.UNDEFINED;
+
+import android.Manifest;
+import android.accounts.AccountManager;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.PropertyInvalidatedCache;
+import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.pm.UserProperties;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
+import android.provider.Settings;
+import android.util.AndroidException;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.WindowManager.LayoutParams;
+
+import com.android.internal.R;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Manages users and user details on a multi-user system. There are two major categories of
+ * users: fully customizable users with their own login, and profiles that share a workspace
+ * with a related user.
+ * <p>
+ * Users are different from accounts, which are managed by
+ * {@link AccountManager}. Each user can have their own set of accounts.
+ * <p>
+ * See {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE} for more on managed profiles.
+ */
+@SystemService(Context.USER_SERVICE)
+public class UserManager {
+
+    private static final String TAG = "UserManager";
+
+    @UnsupportedAppUsage
+    private final IUserManager mService;
+    /** Holding the Application context (not constructor param context). */
+    private final Context mContext;
+
+    /** The userId of the constructor param context. To be used instead of mContext.getUserId(). */
+    private final @UserIdInt int mUserId;
+
+    /** The userType of UserHandle.myUserId(); empty string if not a profile; null until cached. */
+    private String mProfileTypeOfProcessUser = null;
+
+    /** Whether the device is in headless system user mode; null until cached. */
+    private static Boolean sIsHeadlessSystemUser = null;
+
+    /**
+     * User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user.
+     * This type of user cannot be created; it can only pre-exist on first boot.
+     * @hide
+     */
+    @SystemApi
+    public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
+
+    /**
+     * User type representing a regular non-profile non-{@link UserHandle#USER_SYSTEM system} human
+     * user.
+     * This is sometimes called an ordinary 'secondary user'.
+     * @hide
+     */
+    @SystemApi
+    public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
+
+    /**
+     * User type representing a guest user that may be transient.
+     * @hide
+     */
+    @SystemApi
+    public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
+
+    /**
+     * User type representing a user for demo purposes only, which can be removed at any time.
+     * @hide
+     */
+    public static final String USER_TYPE_FULL_DEMO = "android.os.usertype.full.DEMO";
+
+    /**
+     * User type representing a "restricted profile" user, which is a full user that is subject to
+     * certain restrictions from a parent user. Note, however, that it is NOT technically a profile.
+     * @hide
+     */
+    public static final String USER_TYPE_FULL_RESTRICTED = "android.os.usertype.full.RESTRICTED";
+
+    /**
+     * User type representing a managed profile, which is a profile that is to be managed by a
+     * device policy controller (DPC).
+     * The intended purpose is for work profiles, which are managed by a corporate entity.
+     * @hide
+     */
+    @SystemApi
+    public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
+
+    /**
+     * User type representing a clone profile. Clone profile is a user profile type used to run
+     * second instance of an otherwise single user App (eg, messengers). Only the primary user
+     * is allowed to have a clone profile.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
+
+    /**
+     * User type representing a generic profile for testing purposes. Only on debuggable builds.
+     * @hide
+     */
+    public static final String USER_TYPE_PROFILE_TEST = "android.os.usertype.profile.TEST";
+
+    /**
+     * User type representing a {@link UserHandle#USER_SYSTEM system} user that is <b>not</b> a
+     * human user.
+     * This type of user cannot be created; it can only pre-exist on first boot.
+     * @hide
+     */
+    @SystemApi
+    public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
+
+    /**
+     * Flag passed to {@link #requestQuietModeEnabled} to request disabling quiet mode only if
+     * there is no need to confirm the user credentials. If credentials are required to disable
+     * quiet mode, {@link #requestQuietModeEnabled} will do nothing and return {@code false}.
+     */
+    public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 0x1;
+
+    /**
+     * Flag passed to {@link #requestQuietModeEnabled} to request disabling quiet mode without
+     * asking for credentials. This is used when managed profile password is forgotten. It starts
+     * the user in locked state so that a direct boot aware DPC could reset the password.
+     * Should not be used together with
+     * {@link #QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED} or an exception will be thrown.
+     * @hide
+     */
+    public static final int QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL = 0x2;
+
+    /**
+     * List of flags available for the {@link #requestQuietModeEnabled} method.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "QUIET_MODE_" }, value = {
+            QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED,
+            QUIET_MODE_DISABLE_DONT_ASK_CREDENTIAL})
+    public @interface QuietModeFlag {}
+
+    /**
+     * @hide
+     * No user restriction.
+     */
+    @SystemApi
+    public static final int RESTRICTION_NOT_SET = 0x0;
+
+    /**
+     * @hide
+     * User restriction set by system/user.
+     */
+    @SystemApi
+    public static final int RESTRICTION_SOURCE_SYSTEM = 0x1;
+
+    /**
+     * @hide
+     * User restriction set by a device owner.
+     */
+    @SystemApi
+    public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 0x2;
+
+    /**
+     * @hide
+     * User restriction set by a profile owner.
+     */
+    @SystemApi
+    public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 0x4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "RESTRICTION_" }, value = {
+            RESTRICTION_NOT_SET,
+            RESTRICTION_SOURCE_SYSTEM,
+            RESTRICTION_SOURCE_DEVICE_OWNER,
+            RESTRICTION_SOURCE_PROFILE_OWNER
+    })
+    @SystemApi
+    public @interface UserRestrictionSource {}
+
+    /**
+     * Specifies if a user is disallowed from adding and removing accounts, unless they are
+     * {@link android.accounts.AccountManager#addAccountExplicitly programmatically} added by
+     * Authenticator.
+     * The default value is <code>false</code>.
+     *
+     * <p>From {@link android.os.Build.VERSION_CODES#N} a profile or device owner app can still
+     * use {@link android.accounts.AccountManager} APIs to add or remove accounts when account
+     * management is disallowed.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
+
+    /**
+     * Specifies if a user is disallowed from changing Wi-Fi access points via Settings. This
+     * restriction does not affect Wi-Fi tethering settings.
+     *
+     * <p>A device owner and a profile owner can set this restriction, although the restriction has
+     * no effect in a managed profile. When it is set by a device owner, a profile owner on the
+     * primary user or by a profile owner of an organization-owned managed profile on the parent
+     * profile, it disallows the primary user from changing Wi-Fi access points.
+     *
+     * <p>Holders of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WIFI}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+
+    /**
+     * Specifies if a user is disallowed from enabling/disabling Wi-Fi.
+     *
+     * <p>This restriction can only be set by a device owner,
+     * a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by any of these owners, it applies globally - i.e., it disables airplane mode
+     * from changing Wi-Fi state.
+     *
+     * <p>Holders of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WIFI}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state";
+
+    /**
+     * Specifies if a user is disallowed from using Wi-Fi tethering.
+     *
+     * <p>This restriction does not limit the user's ability to modify or connect to regular
+     * Wi-Fi networks, which is separately controlled by {@link #DISALLOW_CONFIG_WIFI}.
+     *
+     * <p>This restriction can only be set by a device owner,
+     * a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by any of these owners, it prevents all users from using
+     * Wi-Fi tethering. Other forms of tethering are not affected.
+     *
+     * <p>Holders of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WIFI}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * This user restriction disables only Wi-Fi tethering.
+     * Use {@link #DISALLOW_CONFIG_TETHERING} to limit all forms of tethering.
+     * When {@link #DISALLOW_CONFIG_TETHERING} is set, this user restriction becomes obsolete.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_WIFI_TETHERING = "no_wifi_tethering";
+
+    /**
+     * Specifies if a user is disallowed from being granted admin privileges.
+     *
+     * <p>This restriction limits ability of other admin users to grant admin
+     * privileges to selected user.
+     *
+     * <p>This restriction has no effect in a mode that does not allow multiple admins.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_GRANT_ADMIN = "no_grant_admin";
+
+    /**
+     * Specifies if users are disallowed from sharing Wi-Fi for admin configured networks.
+     *
+     * <p>Device owner and profile owner can set this restriction.
+     * When it is set by any of these owners, it prevents all users from
+     * sharing Wi-Fi for networks configured by these owners.
+     * Other networks not configured by these owners are not affected.
+     *
+     * <p>Holders of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WIFI}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI =
+            "no_sharing_admin_configured_wifi";
+
+    /**
+     * Specifies if a user is disallowed from using Wi-Fi Direct.
+     *
+     * <p>This restriction can only be set by a device owner,
+     * a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by any of these owners, it prevents all users from using
+     * Wi-Fi Direct.
+     *
+     * <p>Holders of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WIFI}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_WIFI_DIRECT = "no_wifi_direct";
+
+    /**
+     * Specifies if a user is disallowed from adding a new Wi-Fi configuration.
+     *
+     * <p>This restriction can only be set by a device owner,
+     * a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by any of these owners, it prevents all users from adding
+     * a new Wi-Fi configuration. This does not limit the owner and carrier's ability
+     * to add a new configuration.
+     *
+     * <p>Holders of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WIFI}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_ADD_WIFI_CONFIG = "no_add_wifi_config";
+
+    /**
+     * Specifies if a user is disallowed from changing the device
+     * language. The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCALE}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_LOCALE = "no_config_locale";
+
+    /**
+     * Specifies if a user is disallowed from installing applications. This user restriction also
+     * prevents device owners and profile owners installing apps. The default value is
+     * {@code false}.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APPS_CONTROL}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_INSTALL_APPS = "no_install_apps";
+
+    /**
+     * Specifies if a user is disallowed from uninstalling applications.
+     * The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APPS_CONTROL}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
+
+    /**
+     * Specifies if a user is disallowed from turning on location sharing.
+     *
+     * <p>In a managed profile, location sharing by default reflects the primary user's setting, but
+     * can be overridden and forced off by setting this restriction to true in the managed profile.
+     *
+     * <p>A device owner and a profile owner can set this restriction. When it is set by a device
+     * owner, a profile owner on the primary user or by a profile owner of an organization-owned
+     * managed profile on the parent profile, it prevents the primary user from turning on
+     * location sharing.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCATION}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
+
+    /**
+     * Specifies if airplane mode is disallowed on the device.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by any of these owners, it applies globally - i.e., it disables airplane mode
+     * on the entire device.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_AIRPLANE_MODE}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
+
+    /**
+     * Specifies if a user is disallowed from configuring brightness. When device owner sets it,
+     * it'll only be applied on the target(system) user.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_DISPLAY}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>This user restriction has no effect on managed profiles.
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
+
+    /**
+     * Specifies if ambient display is disallowed for the user.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_DISPLAY}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>This user restriction has no effect on managed profiles.
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display";
+
+    /**
+     * Specifies if a user is disallowed from changing screen off timeout.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_DISPLAY}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>This user restriction has no effect on managed profiles.
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
+
+    /**
+     * Specifies if a user is disallowed from enabling the
+     * "Unknown Sources" setting, that allows installation of apps from unknown sources.
+     * Unknown sources exclude adb and special apps such as trusted app stores.
+     * The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
+
+    /**
+     * This restriction is a device-wide version of {@link #DISALLOW_INSTALL_UNKNOWN_SOURCES}.
+     *
+     * Specifies if all users on the device are disallowed from enabling the
+     * "Unknown Sources" setting, that allows installation of apps from unknown sources.
+     *
+     * This restriction can be enabled by the profile owner, in which case all accounts and
+     * profiles will be affected.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY =
+            "no_install_unknown_sources_globally";
+
+    /**
+     * Specifies if a user is disallowed from configuring bluetooth via Settings. This does
+     * <em>not</em> restrict the user from turning bluetooth on or off.
+     *
+     * <p>This restriction doesn't prevent the user from using bluetooth. For disallowing usage of
+     * bluetooth completely on the device, use {@link #DISALLOW_BLUETOOTH}.
+     *
+     * <p>A device owner and a profile owner can set this restriction, although the restriction has
+     * no effect in a managed profile. When it is set by a device owner, a profile owner on the
+     * primary user or by a profile owner of an organization-owned managed profile on the parent
+     * profile, it disallows the primary user from configuring bluetooth.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_BLUETOOTH}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
+
+    /**
+     * Specifies if bluetooth is disallowed on the device. If bluetooth is disallowed on the device,
+     * bluetooth cannot be turned on or configured via Settings.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally - i.e., it disables bluetooth on
+     * the entire device and all users will be affected. When it is set by a profile owner on the
+     * primary user or by a profile owner of an organization-owned managed profile on the parent
+     * profile, it disables the primary user from using bluetooth and configuring bluetooth
+     * in Settings.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_BLUETOOTH}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_BLUETOOTH = "no_bluetooth";
+
+    /**
+     * Specifies if outgoing bluetooth sharing is disallowed.
+     *
+     * <p>A device owner and a profile owner can set this restriction. When it is set by a device
+     * owner, it applies globally. When it is set by a profile owner on the primary user or by a
+     * profile owner of an organization-owned managed profile on the parent profile, it disables
+     * the primary user from any outgoing bluetooth sharing.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_BLUETOOTH}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Default is <code>true</code> for managed profiles and false otherwise.
+     *
+     * <p>When a device upgrades to {@link android.os.Build.VERSION_CODES#O}, the system sets it
+     * for all existing managed profiles.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
+
+    /**
+     * Specifies if a user is disallowed from transferring files over USB.
+     *
+     * <p>This restriction can only be set by a <a href="https://developers.google.com/android/work/terminology#device_owner_do">
+     * device owner</a> or a <a href="https://developers.google.com/android/work/terminology#profile_owner_po">
+     * profile owner</a> on the primary user's profile or a profile owner of an organization-owned
+     * <a href="https://developers.google.com/android/work/terminology#managed_profile">
+     * managed profile</a> on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from transferring files over USB. No other
+     * user on the device is able to use file transfer over USB because the UI for file transfer
+     * is always associated with the primary user.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+
+    /**
+     * Specifies if a user is disallowed from configuring user
+     * credentials. The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
+
+    /**
+     * When set on the admin user this specifies if the user can remove users.
+     * When set on a non-admin secondary user, this specifies if the user can remove itself.
+     * This restriction has no effect on managed profiles.
+     * The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_REMOVE_USER = "no_remove_user";
+
+    /**
+     * Specifies if managed profiles of this user can be removed, other than by its profile owner.
+     * The default value is <code>false</code>.
+     * <p>
+     * This restriction has no effect on managed profiles.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @deprecated As the ability to have a managed profile on a fully-managed device has been
+     * removed from the platform, this restriction will be silently ignored when applied by the
+     * device owner.
+     * When the device is provisioned with a managed profile on an organization-owned device,
+     * the managed profile could not be removed anyway.
+     */
+    @Deprecated
+    public static final String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
+
+    /**
+     * Specifies if a user is disallowed from enabling or accessing debugging features.
+     *
+     * <p>A device owner and a profile owner can set this restriction. When it is set by a device
+     * owner, a profile owner on the primary user or by a profile owner of an organization-owned
+     * managed profile on the parent profile, it disables debugging features altogether, including
+     * USB debugging. When set on a managed profile or a secondary user, it blocks debugging for
+     * that user only, including starting activities, making service calls, accessing content
+     * providers, sending broadcasts, installing/uninstalling packages, clearing user data, etc.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
+
+    /**
+     * Specifies if a user is disallowed from configuring a VPN. The default value is
+     * <code>false</code>. This restriction has an effect when set by device owners and, in Android
+     * 6.0 ({@linkplain android.os.Build.VERSION_CODES#M API level 23}) or higher, profile owners.
+     * <p>This restriction also prevents VPNs from starting. However, in Android 7.0
+     * ({@linkplain android.os.Build.VERSION_CODES#N API level 24}) or higher, the system does
+     * start always-on VPNs created by the device or profile owner.
+     * <p>From Android 12 ({@linkplain android.os.Build.VERSION_CODES#S API level 31}) enforcing
+     * this restriction clears currently active VPN if it was configured by the user.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_VPN}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
+
+    /**
+     * Specifies if a user is disallowed from enabling or disabling location providers. As a
+     * result, user is disallowed from turning on or off location via Settings.
+     *
+     * <p>A device owner and a profile owner can set this restriction. When it is set by a device
+     * owner, a profile owner on the primary user or by a profile owner of an organization-owned
+     * managed profile on the parent profile, it disallows the primary user from turning location
+     * on or off.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCATION}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
+     * as a device owner or a profile owner can still enable or disable location mode via
+     * {@link DevicePolicyManager#setLocationEnabled} when this restriction is on.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see LocationManager#isLocationEnabled()
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_LOCATION = "no_config_location";
+
+    /**
+     * Specifies configuring date, time and timezone is disallowed via Settings.
+     *
+     * <p>A device owner and a profile owner can set this restriction, although the restriction has
+     * no effect in a managed profile. When it is set by a device owner or by a profile owner of an
+     * organization-owned managed profile on the parent profile, it applies globally - i.e.,
+     * it disables date, time and timezone setting on the entire device and all users are affected.
+     * When it is set by a profile owner on the primary user, it disables the primary user
+     * from configuring date, time and timezone and disables all configuring of date, time and
+     * timezone in Settings.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_TIME}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+
+    /**
+     * Specifies if a user is disallowed from using and configuring Tethering and portable hotspots
+     * via Settings.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from using Tethering and hotspots and
+     * disables all configuring of Tethering and hotspots in Settings.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>In Android 9.0 or higher, if tethering is enabled when this restriction is set,
+     * tethering will be automatically turned off.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
+
+    /**
+     * Specifies if a user is disallowed from resetting network settings
+     * from Settings. This can only be set by device owners and profile owners on the primary user.
+     * The default value is <code>false</code>.
+     * <p>This restriction has no effect on secondary users and managed profiles since only the
+     * primary user can reset the network settings of the device.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_NETWORK_RESET = "no_network_reset";
+
+    /**
+     * Specifies if a user is disallowed from factory resetting from Settings.
+     * This can only be set by device owners and profile owners on an admin user.
+     * The default value is <code>false</code>.
+     * <p>This restriction has no effect on non-admin users since they cannot factory reset the
+     * device.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_FACTORY_RESET}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_FACTORY_RESET = "no_factory_reset";
+
+    /**
+     * Specifies if a user is disallowed from adding new users. This can only be set by device
+     * owners or profile owners on the primary user. The default value is <code>false</code>.
+     * <p>This restriction has no effect on secondary users and managed profiles since only the
+     * primary user can add other users.
+     * <p> When the device is an organization-owned device provisioned with a managed profile,
+     * this restriction will be set as a base restriction which cannot be removed by any admin.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_ADD_USER = "no_add_user";
+
+    /**
+     * Specifies if a user is disallowed from adding managed profiles.
+     * <p>The default value for an unmanaged user is <code>false</code>.
+     * For users with a device owner set, the default is <code>true</code>.
+     * <p>This restriction has no effect on managed profiles.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @deprecated As the ability to have a managed profile on a fully-managed device has been
+     * removed from the platform, this restriction will be silently ignored when applied by the
+     * device owner.
+     */
+    @Deprecated
+    public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
+
+    /**
+     * Specifies if a user is disallowed from creating clone profile.
+     * <p>The default value for an unmanaged user is <code>false</code>.
+     * For users with a device owner set, the default is <code>true</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PROFILES}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    public static final String DISALLOW_ADD_CLONE_PROFILE = "no_add_clone_profile";
+
+    /**
+     * Specifies if a user is disallowed from disabling application verification. The default
+     * value is <code>false</code>.
+     *
+     * <p>In Android 8.0 ({@linkplain android.os.Build.VERSION_CODES#O API level 26}) and higher,
+     * this is a global user restriction. If a device owner or profile owner sets this restriction,
+     * the system enforces app verification across all users on the device. Running in earlier
+     * Android versions, this restriction affects only the profile that sets it.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
+
+    /**
+     * Specifies if a user is disallowed from configuring cell broadcasts.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from configuring cell broadcasts.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>This restriction has no effect on secondary users and managed profiles since only the
+     * primary user can configure cell broadcasts.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
+
+    /**
+     * Specifies if a user is disallowed from configuring mobile networks.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from configuring mobile networks.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>This restriction has no effect on secondary users and managed profiles since only the
+     * primary user can configure mobile networks.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+
+    /**
+     * Specifies if a user is disallowed from modifying
+     * applications in Settings or launchers. The following actions will not be allowed when this
+     * restriction is enabled:
+     * <li>uninstalling apps</li>
+     * <li>disabling apps</li>
+     * <li>clearing app caches</li>
+     * <li>clearing app data</li>
+     * <li>force stopping apps</li>
+     * <li>clearing app defaults</li>
+     * <p>
+     * The default value is <code>false</code>.
+     *
+     * <p><strong>Note:</strong> The user will still be able to perform those actions via other
+     * means (such as adb). Third party apps will also be able to uninstall apps via the
+     * {@link android.content.pm.PackageInstaller}. {@link #DISALLOW_UNINSTALL_APPS} or
+     * {@link DevicePolicyManager#setUninstallBlocked(ComponentName, String, boolean)} should be
+     * used to prevent the user from uninstalling apps completely, and
+     * {@link DevicePolicyManager#addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}
+     * to add a default intent handler for a given intent filter.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APPS_CONTROL}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
+
+    /**
+     * Specifies if a user is disallowed from mounting physical external media.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from mounting physical external media.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
+
+    /**
+     * Specifies if a user is disallowed from adjusting microphone volume. If set, the microphone
+     * will be muted.
+     *
+     * <p>A device owner and a profile owner can set this restriction, although the restriction has
+     * no effect in a managed profile. When it is set by a device owner, it applies globally. When
+     * it is set by a profile owner on the primary user or by a profile owner of an
+     * organization-owned managed profile on the parent profile, it will disallow the primary user
+     * from adjusting the microphone volume.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MICROPHONE}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
+
+    /**
+     * Specifies if a user is disallowed from adjusting the global volume. If set, the global volume
+     * will be muted. This can be set by device owners from API 21 and profile owners from API 24.
+     * The default value is <code>false</code>.
+     *
+     * <p>When the restriction is set by profile owners, then it only applies to relevant
+     * profiles.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_AUDIO_OUTPUT}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>This restriction has no effect on managed profiles.
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
+
+    /**
+     * Specifies that the user is not allowed to make outgoing phone calls. Emergency calls are
+     * still permitted.
+     *
+     * <p>A device owner and a profile owner can set this restriction, although the restriction has
+     * no effect in a managed profile. When it is set by a device owner, a profile owner on the
+     * primary user or by a profile owner of an organization-owned managed profile on the parent
+     * profile, it disallows the primary user from making outgoing phone calls.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CALLS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+
+    /**
+     * Specifies that the user is not allowed to send or receive SMS messages.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from sending or receiving SMS messages.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_SMS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SMS = "no_sms";
+
+    /**
+     * Specifies if the user is not allowed to have fun. In some cases, the
+     * device owner may wish to prevent the user from experiencing amusement or
+     * joy while using the device. The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_FUN}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_FUN = "no_fun";
+
+    /**
+     * Specifies that windows besides app windows should not be
+     * created. This will block the creation of the following types of windows.
+     * <li>{@link LayoutParams#TYPE_TOAST}</li>
+     * <li>{@link LayoutParams#TYPE_APPLICATION_OVERLAY}</li>
+     *
+     * <p>This can only be set by device owners and profile owners on the primary user.
+     * The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WINDOWS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
+
+    /**
+     * Specifies that system error dialogs for crashed or unresponsive apps should not be shown.
+     * In this case, the system will force-stop the app as if the user chooses the "close app"
+     * option on the UI. A feedback report isn't collected as there is no way for the user to
+     * provide explicit consent. The default value is <code>false</code>.
+     *
+     * <p>When this user restriction is set by device owners, it's applied to all users. When set by
+     * the profile owner of the primary user or a secondary user, the restriction affects only the
+     * calling user. This user restriction has no effect on managed profiles.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
+
+    /**
+     * Specifies if the clipboard contents can be exported by pasting the data into other users or
+     * profiles. This restriction doesn't prevent import, such as someone pasting clipboard data
+     * from other profiles or users. The default value is {@code false}.
+     *
+     * <p><strong>Note</strong>: Because it's possible to extract data from screenshots using
+     * optical character recognition (OCR), we strongly recommend combining this user restriction
+     * with {@link DevicePolicyManager#setScreenCaptureDisabled(ComponentName, boolean)}.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PROFILE_INTERACTION}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
+
+    /**
+     * Specifies if the user is not allowed to use NFC to beam out data from apps.
+     * The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
+
+    /**
+     * Hidden user restriction to disallow access to wallpaper manager APIs. This restriction
+     * generally means that wallpapers are not supported for the particular user. This user
+     * restriction is always set for managed profiles, because such profiles don't have wallpapers.
+     * @hide
+     * @see #DISALLOW_SET_WALLPAPER
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_WALLPAPER = "no_wallpaper";
+
+    /**
+     * User restriction to disallow setting a wallpaper. Profile owner and device owner
+     * are able to set wallpaper regardless of this restriction.
+     * The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_WALLPAPER}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
+
+    /**
+     * Specifies if the user is not allowed to reboot the device into safe boot mode.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from rebooting the device into safe
+     * boot mode.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_SAFE_BOOT}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SAFE_BOOT = "no_safe_boot";
+
+    /**
+     * Specifies if a user is not allowed to record audio. This restriction is always enabled for
+     * background users. The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final String DISALLOW_RECORD_AUDIO = "no_record_audio";
+
+    /**
+     * Specifies if a user is not allowed to run in the background and should be stopped during
+     * user switch. The default value is <code>false</code>.
+     *
+     * <p>This restriction can be set by device owners and profile owners.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    @SystemApi
+    public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
+
+    /**
+     * Specifies if a user is not allowed to use the camera.
+     *
+     * <p>A device owner and a profile owner can set this restriction. When it is set by a
+     * device owner, it applies globally - i.e., it disables the use of camera on the entire device
+     * and all users are affected. When it is set by a profile owner on the primary user or by a
+     * profile owner of an organization-owned managed profile on the parent profile, it disables
+     * the primary user from using camera.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CAMERA}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    public static final String DISALLOW_CAMERA = "no_camera";
+
+    /**
+     * Specifies if a user is not allowed to unmute the device's global volume.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_AUDIO_OUTPUT}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * @see DevicePolicyManager#setMasterVolumeMuted(ComponentName, boolean)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    public static final String DISALLOW_UNMUTE_DEVICE = "disallow_unmute_device";
+
+    /**
+     * Specifies if a user is not allowed to use cellular data when roaming.
+     *
+     * <p>This restriction can only be set by a device owner, a profile owner on the primary
+     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * When it is set by a device owner, it applies globally. When it is set by a profile owner
+     * on the primary user or by a profile owner of an organization-owned managed profile on
+     * the parent profile, it disables the primary user from using cellular data when roaming.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_DATA_ROAMING = "no_data_roaming";
+
+    /**
+     * Specifies if a user is not allowed to change their icon. Device owner and profile owner
+     * can set this restriction. When it is set by device owner, only the target user will be
+     * affected. The default value is <code>false</code>.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SET_USER_ICON = "no_set_user_icon";
+
+    /**
+     * Specifies if a user is not allowed to enable the oem unlock setting. The default value is
+     * <code>false</code>. Setting this restriction has no effect if the bootloader is already
+     * unlocked.
+     *
+     * <p>Not for use by third-party applications.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @deprecated use {@link OemLockManager#setOemUnlockAllowedByCarrier(boolean, byte[])} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
+
+    /**
+     * Specifies that the managed profile is not allowed to have unified lock screen challenge with
+     * the primary user.
+     *
+     * <p><strong>Note:</strong> Setting this restriction alone doesn't automatically set a
+     * separate challenge. Profile owner can ask the user to set a new password using
+     * {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD} and verify it using
+     * {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)}.
+     *
+     * <p>Can be set by profile owners. It only has effect on managed profiles when set by managed
+     * profile owner. Has no effect on non-managed profiles or users.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
+
+    /**
+     * Allows apps in the parent profile to handle web links from the managed profile.
+     *
+     * This user restriction has an effect only in a managed profile.
+     * If set:
+     * Intent filters of activities in the parent profile with action
+     * {@link android.content.Intent#ACTION_VIEW},
+     * category {@link android.content.Intent#CATEGORY_BROWSABLE}, scheme http or https, and which
+     * define a host can handle intents from the managed profile.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PROFILES}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String ALLOW_PARENT_PROFILE_APP_LINKING
+            = "allow_parent_profile_app_linking";
+
+    /**
+     * Specifies if a user is not allowed to use Autofill Services.
+     *
+     * <p>Device owner and profile owner can set this restriction. When it is set by device owner,
+     * only the target user will be affected.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_AUTOFILL}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_AUTOFILL = "no_autofill";
+
+    /**
+     * Specifies if the contents of a user's screen is not allowed to be captured for artificial
+     * intelligence purposes.
+     *
+     * <p>A device owner and a profile owner can set this restriction. When it is set by a device
+     * owner, a profile owner on the primary user or by a profile owner of an organization-owned
+     * managed profile on the parent profile, it disables the primary user's screen from being
+     * captured for artificial intelligence purposes.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_SCREEN_CONTENT}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONTENT_CAPTURE = "no_content_capture";
+
+    /**
+     * Specifies if the current user is able to receive content suggestions for selections based on
+     * the contents of their screen.
+     *
+     * <p>A device owner and a profile owner can set this restriction. When it is set by a device
+     * owner, a profile owner on the primary user or by a profile owner of an organization-owned
+     * managed profile on the parent profile, it disables the primary user from receiving content
+     * suggestions for selections based on the contents of their screen.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_SCREEN_CONTENT}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONTENT_SUGGESTIONS = "no_content_suggestions";
+
+    /**
+     * Specifies if user switching is blocked on the current user.
+     *
+     * <p> This restriction can only be set by the device owner, it will be applied to all users.
+     * Device owner can still switch user via
+     * {@link DevicePolicyManager#switchUser(ComponentName, UserHandle)} when this restriction is
+     * set.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_USER_SWITCH = "no_user_switch";
+
+    /**
+     * Specifies whether the user can share file / picture / data from the primary user into the
+     * managed profile, either by sending them from the primary side, or by picking up data within
+     * an app in the managed profile.
+     * <p>
+     * When a managed profile is created, the system allows the user to send data from the primary
+     * side to the profile by setting up certain default cross profile intent filters. If
+     * this is undesired, this restriction can be set to disallow it. Note that this restriction
+     * will not block any sharing allowed by explicit
+     * {@link DevicePolicyManager#addCrossProfileIntentFilter} calls by the profile owner.
+     * <p>
+     * This restriction is only meaningful when set by profile owner. When it is set by device
+     * owner, it does not have any effect.
+     * <p>
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PROFILE_INTERACTION}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
+
+    /**
+     * Specifies whether the user is allowed to print.
+     *
+     * This restriction can be set by device or profile owner.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PRINTING}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * The default value is {@code false}.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_PRINTING = "no_printing";
+
+    /**
+     * Specifies whether the user is allowed to modify private DNS settings.
+     *
+     * <p>This restriction can only be set by a device owner or a profile owner of an
+     * organization-owned managed profile on the parent profile. When it is set by either of these
+     * owners, it applies globally.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_PRIVATE_DNS =
+            "disallow_config_private_dns";
+
+    /**
+     * Specifies whether the microphone toggle is available to the user. If this restriction is set,
+     * the user will not be able to block microphone access via the system toggle. If microphone
+     * access is blocked when the restriction is added, it will be automatically re-enabled.
+     *
+     * This restriction can only be set by a device owner.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see android.hardware.SensorPrivacyManager
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_MICROPHONE_TOGGLE =
+            "disallow_microphone_toggle";
+
+    /**
+     * Specifies whether the camera toggle is available to the user. If this restriction is set,
+     * the user will not be able to block camera access via the system toggle. If camera
+     * access is blocked when the restriction is added, it will be automatically re-enabled.
+     *
+     * This restriction can only be set by a device owner.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see android.hardware.SensorPrivacyManager
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CAMERA_TOGGLE =
+            "disallow_camera_toggle";
+
+    /**
+     * This is really not a user restriction in the normal sense. This can't be set to a user,
+     * via UserManager nor via DevicePolicyManager. This is not even set in UserSettingsUtils.
+     * This is defined here purely for convenience within the settings app.
+     *
+     * TODO(b/191306258): Refactor the Settings app to remove the need for this field, and delete it
+     *
+     * Specifies whether biometrics are available to the user. This is used internally only,
+     * as a means of communications between biometric settings and
+     * {@link com.android.settingslib.enterprise.ActionDisabledByAdminControllerFactory}.
+     *
+     * @see {@link android.hardware.biometrics.ParentalControlsUtilsInternal}
+     * @see {@link com.android.settings.biometrics.ParentalControlsUtils}
+     *
+     * @hide
+     */
+    public static final String DISALLOW_BIOMETRIC = "disallow_biometric";
+
+    /**
+     * Specifies whether the user is allowed to modify default apps in settings.
+     *
+     * <p>This restriction can be set by device or profile owner.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_DEFAULT_APPS = "disallow_config_default_apps";
+
+    /**
+     * Application restriction key that is used to indicate the pending arrival
+     * of real restrictions for the app.
+     *
+     * <p>
+     * Applications that support restrictions should check for the presence of this key.
+     * A <code>true</code> value indicates that restrictions may be applied in the near
+     * future but are not available yet. It is the responsibility of any
+     * management application that sets this flag to update it when the final
+     * restrictions are enforced.
+     *
+     * <p>Key for application restrictions.
+     * <p>Type: Boolean
+     * @see android.app.admin.DevicePolicyManager#setApplicationRestrictions(
+     *      android.content.ComponentName, String, Bundle)
+     * @see android.app.admin.DevicePolicyManager#getApplicationRestrictions(
+     *      android.content.ComponentName, String)
+     */
+    public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
+
+    /**
+     * Specifies if a user is not allowed to use 2g networks.
+     *
+     * <p>This restriction can only be set by a device owner or a profile owner of an
+     * organization-owned managed profile on the parent profile.
+     * In all cases, the setting applies globally on the device and will prevent the device from
+     * scanning for or connecting to 2g networks, except in the case of an emergency.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
+
+    /**
+     * This user restriction specifies if Ultra-wideband is disallowed on the device. If
+     * Ultra-wideband is disallowed it cannot be turned on via Settings.
+     *
+     * <p>
+     * Ultra-wideband (UWB) is a radio technology that can use a very low energy level
+     * for short-range, high-bandwidth communications over a large portion of the radio spectrum.
+     *
+     * <p>This restriction can only be set by a device owner or a profile owner of an
+     * organization-owned managed profile on the parent profile.
+     * In both cases, the restriction applies globally on the device and will turn off the
+     * ultra-wideband radio if it's currently on and prevent the radio from being turned on in
+     * the future.
+     *
+     * <p>Holders of the permission
+     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION}
+     * can set this restriction using the DevicePolicyManager APIs mentioned below.
+     *
+     * <p>Default is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_ULTRA_WIDEBAND_RADIO = "no_ultra_wideband_radio";
+
+    /**
+     * List of key values that can be passed into the various user restriction related methods
+     * in {@link UserManager} & {@link DevicePolicyManager}.
+     * Note: This is slightly different from the real set of user restrictions listed in {@link
+     * com.android.server.pm.UserRestrictionsUtils#USER_RESTRICTIONS}. For example
+     * {@link #KEY_RESTRICTIONS_PENDING} is not a real user restriction, but is a legitimate
+     * value that can be passed into {@link #hasUserRestriction(String)}.
+     * @hide
+     */
+    @StringDef(value = {
+            DISALLOW_MODIFY_ACCOUNTS,
+            DISALLOW_CONFIG_WIFI,
+            DISALLOW_CONFIG_LOCALE,
+            DISALLOW_INSTALL_APPS,
+            DISALLOW_UNINSTALL_APPS,
+            DISALLOW_SHARE_LOCATION,
+            DISALLOW_AIRPLANE_MODE,
+            DISALLOW_CONFIG_BRIGHTNESS,
+            DISALLOW_AMBIENT_DISPLAY,
+            DISALLOW_CONFIG_SCREEN_TIMEOUT,
+            DISALLOW_INSTALL_UNKNOWN_SOURCES,
+            DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
+            DISALLOW_CONFIG_BLUETOOTH,
+            DISALLOW_BLUETOOTH,
+            DISALLOW_BLUETOOTH_SHARING,
+            DISALLOW_USB_FILE_TRANSFER,
+            DISALLOW_CONFIG_CREDENTIALS,
+            DISALLOW_REMOVE_USER,
+            DISALLOW_REMOVE_MANAGED_PROFILE,
+            DISALLOW_DEBUGGING_FEATURES,
+            DISALLOW_CONFIG_VPN,
+            DISALLOW_CONFIG_LOCATION,
+            DISALLOW_CONFIG_DATE_TIME,
+            DISALLOW_CONFIG_TETHERING,
+            DISALLOW_NETWORK_RESET,
+            DISALLOW_FACTORY_RESET,
+            DISALLOW_ADD_USER,
+            DISALLOW_ADD_MANAGED_PROFILE,
+            DISALLOW_ADD_CLONE_PROFILE,
+            ENSURE_VERIFY_APPS,
+            DISALLOW_CONFIG_CELL_BROADCASTS,
+            DISALLOW_CONFIG_MOBILE_NETWORKS,
+            DISALLOW_APPS_CONTROL,
+            DISALLOW_MOUNT_PHYSICAL_MEDIA,
+            DISALLOW_UNMUTE_MICROPHONE,
+            DISALLOW_ADJUST_VOLUME,
+            DISALLOW_OUTGOING_CALLS,
+            DISALLOW_SMS,
+            DISALLOW_FUN,
+            DISALLOW_CREATE_WINDOWS,
+            DISALLOW_SYSTEM_ERROR_DIALOGS,
+            DISALLOW_CROSS_PROFILE_COPY_PASTE,
+            DISALLOW_OUTGOING_BEAM,
+            DISALLOW_WALLPAPER,
+            DISALLOW_SET_WALLPAPER,
+            DISALLOW_SAFE_BOOT,
+            DISALLOW_RECORD_AUDIO,
+            DISALLOW_RUN_IN_BACKGROUND,
+            DISALLOW_CAMERA,
+            DISALLOW_UNMUTE_DEVICE,
+            DISALLOW_DATA_ROAMING,
+            DISALLOW_SET_USER_ICON,
+            DISALLOW_OEM_UNLOCK,
+            DISALLOW_UNIFIED_PASSWORD,
+            ALLOW_PARENT_PROFILE_APP_LINKING,
+            DISALLOW_AUTOFILL,
+            DISALLOW_CONTENT_CAPTURE,
+            DISALLOW_CONTENT_SUGGESTIONS,
+            DISALLOW_USER_SWITCH,
+            DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+            DISALLOW_PRINTING,
+            DISALLOW_CONFIG_PRIVATE_DNS,
+            DISALLOW_MICROPHONE_TOGGLE,
+            DISALLOW_CAMERA_TOGGLE,
+            KEY_RESTRICTIONS_PENDING,
+            DISALLOW_BIOMETRIC,
+            DISALLOW_CHANGE_WIFI_STATE,
+            DISALLOW_WIFI_TETHERING,
+            DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,
+            DISALLOW_WIFI_DIRECT,
+            DISALLOW_ADD_WIFI_CONFIG,
+            DISALLOW_CELLULAR_2G,
+            DISALLOW_ULTRA_WIDEBAND_RADIO,
+            DISALLOW_GRANT_ADMIN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UserRestrictionKey {}
+
+    /**
+     * Property used to override whether the device uses headless system user mode.
+     *
+     * <p>Only used on non-user builds.
+     *
+     * <p><b>NOTE: </b>setting this variable directly won't properly change the headless system user
+     * mode behavior and might put the device in a bad state; the system user mode should be changed
+     * using {@code cmd user set-system-user-mode-emulation} instead.
+     *
+     * @hide
+     */
+    public static final String SYSTEM_USER_MODE_EMULATION_PROPERTY =
+            "persist.debug.user_mode_emulation";
+
+    /** @hide */
+    public static final String SYSTEM_USER_MODE_EMULATION_DEFAULT = "default";
+    /** @hide */
+    public static final String SYSTEM_USER_MODE_EMULATION_FULL = "full";
+    /** @hide */
+    public static final String SYSTEM_USER_MODE_EMULATION_HEADLESS = "headless";
+
+    /**
+     * System Property used to override whether users can be created even if their type is disabled
+     * or their limit is reached. Set value to 1 to enable.
+     *
+     * <p>Only used on non-user builds.
+     *
+     * @hide
+     */
+    public static final String DEV_CREATE_OVERRIDE_PROPERTY = "debug.user.creation_override";
+
+    private static final String ACTION_CREATE_USER = "android.os.action.CREATE_USER";
+
+    /**
+     * Action to start an activity to create a supervised user.
+     * Only devices with non-empty config_supervisedUserCreationPackage support this.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
+    public static final String ACTION_CREATE_SUPERVISED_USER =
+            "android.os.action.CREATE_SUPERVISED_USER";
+
+    /**
+     * Extra containing a name for the user being created. Optional parameter passed to
+     * ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_NAME = "android.os.extra.USER_NAME";
+
+    /**
+     * Extra containing account name for the user being created. Optional parameter passed to
+     * ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_ACCOUNT_NAME = "android.os.extra.USER_ACCOUNT_NAME";
+
+    /**
+     * Extra containing account type for the user being created. Optional parameter passed to
+     * ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_ACCOUNT_TYPE = "android.os.extra.USER_ACCOUNT_TYPE";
+
+    /**
+     * Extra containing account-specific data for the user being created. Optional parameter passed
+     * to ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_ACCOUNT_OPTIONS
+            = "android.os.extra.USER_ACCOUNT_OPTIONS";
+
+    /** @hide */
+    public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
+    /** @hide */
+    public static final int PIN_VERIFICATION_FAILED_NOT_SET = -2;
+    /** @hide */
+    public static final int PIN_VERIFICATION_SUCCESS = -1;
+
+    /**
+     * Sent when user restrictions have changed.
+     *
+     * @hide
+     */
+    @SystemApi // To allow seeing it from CTS.
+    public static final String ACTION_USER_RESTRICTIONS_CHANGED =
+            "android.os.action.USER_RESTRICTIONS_CHANGED";
+
+    /**
+     * Error result indicating that this user is not allowed to add other users on this device.
+     * This is a result code returned from the activity created by the intent
+     * {@link #createUserCreationIntent(String, String, String, PersistableBundle)}.
+     */
+    public static final int USER_CREATION_FAILED_NOT_PERMITTED = Activity.RESULT_FIRST_USER;
+
+    /**
+     * Error result indicating that no more users can be created on this device.
+     * This is a result code returned from the activity created by the intent
+     * {@link #createUserCreationIntent(String, String, String, PersistableBundle)}.
+     */
+    public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1;
+
+    /**
+     * Indicates that users are switchable.
+     * @hide
+     */
+    @SystemApi
+    public static final int SWITCHABILITY_STATUS_OK = 0;
+
+    /**
+     * Indicated that the user is in a phone call.
+     * @hide
+     */
+    @SystemApi
+    public static final int SWITCHABILITY_STATUS_USER_IN_CALL = 1 << 0;
+
+    /**
+     * Indicates that user switching is disallowed ({@link #DISALLOW_USER_SWITCH} is set).
+     * @hide
+     */
+    @SystemApi
+    public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 1 << 1;
+
+    /**
+     * Indicates that the system user is locked and user switching is not allowed.
+     * @hide
+     */
+    @SystemApi
+    public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 1 << 2;
+
+    /**
+     * Result returned in {@link #getUserSwitchability()} indicating user switchability.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "SWITCHABILITY_STATUS_" }, value = {
+            SWITCHABILITY_STATUS_OK,
+            SWITCHABILITY_STATUS_USER_IN_CALL,
+            SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED,
+            SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED
+    })
+    public @interface UserSwitchabilityResult {}
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * the specified user has been successfully removed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_REMOVED = 0;
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * the specified user is marked so that it will be removed when the user is stopped or on boot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_DEFERRED = 1;
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * the specified user is already in the process of being removed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2;
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * an unknown error occurred that prevented the user from being removed or set as ephemeral.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_ERROR_UNKNOWN = -1;
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * the user could not be removed due to a {@link #DISALLOW_REMOVE_MANAGED_PROFILE} or
+     * {@link #DISALLOW_REMOVE_USER} user restriction.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_ERROR_USER_RESTRICTION = -2;
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * user being removed does not exist.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_ERROR_USER_NOT_FOUND = -3;
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * user being removed is a {@link UserHandle#SYSTEM} user which can't be removed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_ERROR_SYSTEM_USER = -4;
+
+    /**
+     * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
+     * user being removed is a  {@link UserInfo#FLAG_MAIN}  user and can't be removed because
+     * system property {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true.
+     * @hide
+     */
+    @SystemApi
+    public static final int REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN = -5;
+
+    /**
+     * Possible response codes from {@link #removeUserWhenPossible(UserHandle, boolean)}.
+     *
+     * @hide
+     */
+    @IntDef(prefix = { "REMOVE_RESULT_" }, value = {
+            REMOVE_RESULT_REMOVED,
+            REMOVE_RESULT_DEFERRED,
+            REMOVE_RESULT_ALREADY_BEING_REMOVED,
+            REMOVE_RESULT_ERROR_USER_RESTRICTION,
+            REMOVE_RESULT_ERROR_USER_NOT_FOUND,
+            REMOVE_RESULT_ERROR_SYSTEM_USER,
+            REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN,
+            REMOVE_RESULT_ERROR_UNKNOWN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RemoveResult {}
+
+    /**
+     * Indicates user operation is successful.
+     */
+    public static final int USER_OPERATION_SUCCESS = 0;
+
+    /**
+     * Indicates user operation failed for unknown reason.
+     */
+    public static final int USER_OPERATION_ERROR_UNKNOWN = 1;
+
+    /**
+     * Indicates user operation failed because target user is a managed profile.
+     */
+    public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;
+
+    /**
+     * Indicates user operation failed because maximum running user limit has been reached.
+     */
+    public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;
+
+    /**
+     * Indicates user operation failed because the target user is in the foreground.
+     */
+    public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;
+
+    /**
+     * Indicates user operation failed because device has low data storage.
+     */
+    public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5;
+
+    /**
+     * Indicates user operation failed because maximum user limit has been reached.
+     */
+    public static final int USER_OPERATION_ERROR_MAX_USERS = 6;
+
+    /**
+     * Indicates user operation failed because a user with that account already exists.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7;
+
+    /**
+     * Result returned from various user operations.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "USER_OPERATION_" }, value = {
+            USER_OPERATION_SUCCESS,
+            USER_OPERATION_ERROR_UNKNOWN,
+            USER_OPERATION_ERROR_MANAGED_PROFILE,
+            USER_OPERATION_ERROR_MAX_RUNNING_USERS,
+            USER_OPERATION_ERROR_CURRENT_USER,
+            USER_OPERATION_ERROR_LOW_STORAGE,
+            USER_OPERATION_ERROR_MAX_USERS,
+            USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS
+    })
+    public @interface UserOperationResult {}
+
+    /**
+     * Thrown to indicate user operation failed.
+     */
+    public static class UserOperationException extends RuntimeException {
+        private final @UserOperationResult int mUserOperationResult;
+
+        /**
+         * Constructs a UserOperationException with specific result code.
+         *
+         * @param message the detail message
+         * @param userOperationResult the result code
+         * @hide
+         */
+        public UserOperationException(String message,
+                @UserOperationResult int userOperationResult) {
+            super(message);
+            mUserOperationResult = userOperationResult;
+        }
+
+        /**
+         * Returns the operation result code.
+         */
+        public @UserOperationResult int getUserOperationResult() {
+            return mUserOperationResult;
+        }
+
+        /**
+         * Returns a UserOperationException containing the same message and error code.
+         * @hide
+         */
+        public static UserOperationException from(ServiceSpecificException exception) {
+            return new UserOperationException(exception.getMessage(), exception.errorCode);
+        }
+    }
+
+    /**
+     * Converts the ServiceSpecificException into a UserOperationException or throws null;
+     *
+     * @param exception exception to convert.
+     * @param throwInsteadOfNull if an exception should be thrown or null returned.
+     * @return null if chosen not to throw exception.
+     * @throws UserOperationException
+     */
+    private <T> T returnNullOrThrowUserOperationException(ServiceSpecificException exception,
+            boolean throwInsteadOfNull) throws UserOperationException {
+        if (throwInsteadOfNull) {
+            throw UserOperationException.from(exception);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Thrown to indicate user operation failed. (Checked exception)
+     * @hide
+     */
+    public static class CheckedUserOperationException extends AndroidException {
+        private final @UserOperationResult int mUserOperationResult;
+
+        /**
+         * Constructs a CheckedUserOperationException with specific result code.
+         *
+         * @param message the detail message
+         * @param userOperationResult the result code
+         * @hide
+         */
+        public CheckedUserOperationException(String message,
+                @UserOperationResult int userOperationResult) {
+            super(message);
+            mUserOperationResult = userOperationResult;
+        }
+
+        /** Returns the operation result code. */
+        public @UserOperationResult int getUserOperationResult() {
+            return mUserOperationResult;
+        }
+
+        /** Return a ServiceSpecificException containing the same message and error code. */
+        public ServiceSpecificException toServiceSpecificException() {
+            return new ServiceSpecificException(mUserOperationResult, getMessage());
+        }
+    }
+
+    /**
+     * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} and above, any UserManager API marked
+     * as {@link  android.annotation.UserHandleAware @UserHandleAware} will use the context user
+     * (rather than the calling user).
+     * For apps targeting an SDK version <em>below</em> this, the behaviour
+     * depends on the particular method and when it was first introduced:
+     * <ul>
+     *     <li>
+     *         if the {@literal @}UserHandleAware specifies a
+     *         {@link  android.annotation.UserHandleAware#enabledSinceTargetSdkVersion} of
+     *         {@link Build.VERSION_CODES#TIRAMISU} the <em>calling</em> user is used.
+     *     </li>
+     *     <li>
+     *         if the {@literal @}UserHandleAware doesn't specify a
+     *         {@link  android.annotation.UserHandleAware#enabledSinceTargetSdkVersion}, the
+     *         <em>context</em> user is used.
+     *     </li>
+     *     <li>there should currently be no other values used by UserManager for
+     *         {@link  android.annotation.UserHandleAware#enabledSinceTargetSdkVersion}, since all
+     *         old implicitly user-dependant APIs were updated in that version and anything
+     *         introduced more recently should already be {@literal @}UserHandleAware.
+     *     </li>
+     * </ul>
+     *
+     * Note that when an API marked with
+     * {@link  android.annotation.UserHandleAware#enabledSinceTargetSdkVersion} is run
+     * on a device whose OS predates that version, the calling user will be used, since on such a
+     * device, the API is not {@literal @}UserHandleAware yet.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long ALWAYS_USE_CONTEXT_USER = 183155436L;
+
+    /**
+     * Returns the context user or the calling user, depending on the target SDK.
+     * New APIs do not require such gating and therefore should always use mUserId instead.
+     * @see #ALWAYS_USE_CONTEXT_USER
+     */
+    private @UserIdInt int getContextUserIfAppropriate() {
+        if (CompatChanges.isChangeEnabled(ALWAYS_USE_CONTEXT_USER)) {
+            return mUserId;
+        } else {
+            final int callingUser = UserHandle.myUserId();
+            if (callingUser != mUserId) {
+                Log.w(TAG, "Using the calling user " + callingUser
+                        + ", rather than the specified context user " + mUserId
+                        + ", because API is only UserHandleAware on higher targetSdkVersions.",
+                        new Throwable());
+            }
+            return callingUser;
+        }
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static UserManager get(Context context) {
+        return (UserManager) context.getSystemService(Context.USER_SERVICE);
+    }
+
+    /** @hide */
+    public UserManager(Context context, IUserManager service) {
+        mService = service;
+        Context appContext = context.getApplicationContext();
+        mContext = (appContext == null ? context : appContext);
+        mUserId = context.getUserId();
+    }
+
+    /**
+     * Returns whether this device supports multiple users with their own login and customizable
+     * space.
+     * @return whether the device supports multiple users.
+     */
+    public static boolean supportsMultipleUsers() {
+        return getMaxSupportedUsers() > 1
+                && SystemProperties.getBoolean("fw.show_multiuserui",
+                Resources.getSystem().getBoolean(R.bool.config_enableMultiUserUI));
+    }
+
+    /**
+     * @return Whether guest user is always ephemeral
+     * @hide
+     */
+    public static boolean isGuestUserAlwaysEphemeral() {
+        return Resources.getSystem()
+                .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+    }
+
+    /**
+     * @return true, when we want to enable user manager API and UX to allow
+     *           guest user ephemeral state change based on user input
+     * @hide
+     */
+    public static boolean isGuestUserAllowEphemeralStateChange() {
+        return Resources.getSystem()
+                .getBoolean(com.android.internal.R.bool.config_guestUserAllowEphemeralStateChange);
+    }
+
+    /**
+     * Returns whether multiple admins are enabled on the device
+     * @hide
+     */
+    public static boolean isMultipleAdminEnabled() {
+        return Resources.getSystem()
+                .getBoolean(com.android.internal.R.bool.config_enableMultipleAdmins);
+    }
+
+    /**
+     * Checks whether the device is running in a headless system user mode.
+     *
+     * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
+     * services and some system UI, but it is not associated with any real person and additional
+     * users must be created to be associated with real persons.
+     *
+     * @return whether the device is running in a headless system user mode.
+     */
+    public static boolean isHeadlessSystemUserMode() {
+        // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
+        // (Its value is determined when UMS is constructed and cannot change.)
+        // Worst case we might end up calling the AIDL method multiple times but that's fine.
+        if (sIsHeadlessSystemUser == null) {
+            // Unfortunately this API is static, but the property no longer is. So go fetch the UMS.
+            try {
+                final IUserManager service = IUserManager.Stub.asInterface(
+                        ServiceManager.getService(Context.USER_SERVICE));
+                sIsHeadlessSystemUser = service.isHeadlessSystemUserMode();
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return sIsHeadlessSystemUser;
+    }
+
+    /**
+     * @deprecated use {@link #getUserSwitchability()} instead.
+     *
+     * @removed
+     * @hide
+     */
+    @Deprecated
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UserHandleAware
+    public boolean canSwitchUsers() {
+        try {
+            return mService.getUserSwitchability(mUserId) == SWITCHABILITY_STATUS_OK;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether switching users is currently allowed for the context user.
+     * <p>
+     * Switching users is not allowed in the following cases:
+     * <li>the user is in a phone call</li>
+     * <li>{@link #DISALLOW_USER_SWITCH} is set</li>
+     * <li>system user hasn't been unlocked yet</li>
+     *
+     * @return A {@link UserSwitchabilityResult} flag indicating if the user is switchable.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public @UserSwitchabilityResult int getUserSwitchability() {
+        return getUserSwitchability(UserHandle.of(getContextUserIfAppropriate()));
+    }
+
+    /**
+     * Returns whether switching users is currently allowed for the provided user.
+     * <p>
+     * Switching users is not allowed in the following cases:
+     * <li>the user is in a phone call</li>
+     * <li>{@link #DISALLOW_USER_SWITCH} is set</li>
+     * <li>system user hasn't been unlocked yet</li>
+     *
+     * @return A {@link UserSwitchabilityResult} flag indicating if the user is switchable.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public @UserSwitchabilityResult int getUserSwitchability(UserHandle userHandle) {
+        try {
+            return mService.getUserSwitchability(userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the userId for the context user.
+     *
+     * @return the userId of the context user.
+     *
+     * @deprecated To get the <em>calling</em> user, use {@link UserHandle#myUserId()}.
+     *             To get the <em>context</em> user, get it directly from the context.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    // *** Do NOT use this in UserManager. Instead always use mUserId. ***
+    public @UserIdInt int getUserHandle() {
+        return getContextUserIfAppropriate();
+    }
+
+    /**
+     * Returns the userId for the user that this process is running under
+     * (<em>not</em> the context user).
+     *
+     * @return the userId of <em>this process</em>.
+     *
+     * @deprecated Use {@link UserHandle#myUserId()}
+     * @hide
+     */
+    @Deprecated
+    // NOT @UserHandleAware
+    public @UserIdInt int getProcessUserId() {
+        return UserHandle.myUserId();
+    }
+
+    /**
+     * @return the user type of the context user.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS})
+    @UserHandleAware
+    public @NonNull String getUserType() {
+        UserInfo userInfo = getUserInfo(mUserId);
+        return userInfo == null ? "" : userInfo.userType;
+    }
+
+    /**
+     * Returns the user name of the context user. This call is only available to applications on
+     * the system image.
+     *
+     * @return the user name
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.CREATE_USERS,
+                    android.Manifest.permission.QUERY_USERS})
+    public @NonNull String getUserName() {
+        if (UserHandle.myUserId() == mUserId) {
+            try {
+                return mService.getUserName();
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        } else {
+            UserInfo userInfo = getUserInfo(mUserId);
+            if (userInfo != null && userInfo.name != null) {
+                return userInfo.name;
+            }
+            return "";
+        }
+    }
+
+    /**
+     * Returns whether user name has been set.
+     * <p>This method can be used to check that the value returned by {@link #getUserName()} was
+     * set by the user and is not a placeholder string provided by the system.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.CREATE_USERS,
+                    android.Manifest.permission.QUERY_USERS})
+    public boolean isUserNameSet() {
+        try {
+            return mService.isUserNameSet(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Used to determine whether the user making this call is subject to
+     * teleportations.
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method can
+     * now automatically identify goats using advanced goat recognition technology.</p>
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#R}, this method always returns
+     * {@code false} in order to protect goat privacy.</p>
+     *
+     * @return Returns whether the user making this call is a goat.
+     */
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public boolean isUserAGoat() {
+        if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
+            return false;
+        }
+        // Caution: This is NOT @UserHandleAware (because mContext is getApplicationContext and
+        // can hold a different userId), but for R+ it returns false, so it doesn't matter anyway.
+        return mContext.getPackageManager()
+                .isPackageAvailable("com.coffeestainstudios.goatsimulator");
+    }
+
+    /**
+     * Used to check if the context user is the primary user. The primary user is the first human
+     * user on a device. This is not supported in headless system user mode.
+     *
+     * @return whether the context user is the primary user.
+     *
+     * @deprecated This method always returns true for the system user, who may not be a full user
+     * if {@link #isHeadlessSystemUserMode} is true. Use {@link #isSystemUser}, {@link #isAdminUser}
+     * or {@link #isMainUser} instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public boolean isPrimaryUser() {
+        final UserInfo user = getUserInfo(getContextUserIfAppropriate());
+        return user != null && user.isPrimary();
+    }
+
+    /**
+     * Used to check if the context user is the system user. The system user
+     * is the initial user that is implicitly created on first boot and hosts most of the
+     * system services.
+     *
+     * @return whether the context user is the system user.
+     */
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public boolean isSystemUser() {
+        return getContextUserIfAppropriate() == UserHandle.USER_SYSTEM;
+    }
+
+    /**
+     * Returns {@code true} if the context user is the designated "main user" of the device. This
+     * user may have access to certain features which are limited to at most one user. There will
+     * never be more than one main user on a device.
+     *
+     * <p>Currently, on most form factors the first human user on the device will be the main user;
+     * in the future, the concept may be transferable, so a different user (or even no user at all)
+     * may be designated the main user instead. On other form factors there might not be a main
+     * user.
+     *
+     * <p>Note that this will not be the system user on devices for which
+     * {@link #isHeadlessSystemUserMode()} returns true.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    @UserHandleAware
+    public boolean isMainUser() {
+        final UserInfo user = getUserInfo(mUserId);
+        return user != null && user.isMain();
+    }
+
+    /**
+     * Returns the designated "main user" of the device, or {@code null} if there is no main user.
+     *
+     * @see #isMainUser()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    public @Nullable UserHandle getMainUser() {
+        try {
+            final int mainUserId = mService.getMainUserId();
+            if (mainUserId == UserHandle.USER_NULL) {
+                return null;
+            }
+            return UserHandle.of(mainUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Used to check if the context user is an admin user. An admin user may be allowed to
+     * modify or configure certain settings that aren't available to non-admin users,
+     * create and delete additional users, etc. There can be more than one admin users.
+     *
+     * @return whether the context user is an admin user.
+     */
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
+    public boolean isAdminUser() {
+        try {
+            return mService.isAdminUser(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Returns whether the provided user is an admin user. There can be more than one admin
+     * user.
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    public boolean isUserAdmin(@UserIdInt int userId) {
+        UserInfo user = getUserInfo(userId);
+        return user != null && user.isAdmin();
+    }
+
+    /**
+     * Returns whether the context user is of the given user type.
+     *
+     * @param userType the name of the user's user type, e.g.
+     *                 {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @return true if the user is of the given user type.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS})
+    @UserHandleAware
+    public boolean isUserOfType(@NonNull String userType) {
+        try {
+            return mService.isUserOfType(mUserId, userType);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the user type is a
+     * {@link UserManager#USER_TYPE_PROFILE_MANAGED managed profile}.
+     * @hide
+     */
+    public static boolean isUserTypeManagedProfile(@Nullable String userType) {
+        return USER_TYPE_PROFILE_MANAGED.equals(userType);
+    }
+
+    /**
+     * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_GUEST guest user}.
+     * @hide
+     */
+    public static boolean isUserTypeGuest(@Nullable String userType) {
+        return USER_TYPE_FULL_GUEST.equals(userType);
+    }
+
+    /**
+     * Returns whether the user type is a
+     * {@link UserManager#USER_TYPE_FULL_RESTRICTED restricted user}.
+     * @hide
+     */
+    public static boolean isUserTypeRestricted(@Nullable String userType) {
+        return USER_TYPE_FULL_RESTRICTED.equals(userType);
+    }
+
+    /**
+     * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_DEMO demo user}.
+     * @hide
+     */
+    public static boolean isUserTypeDemo(@Nullable String userType) {
+        return USER_TYPE_FULL_DEMO.equals(userType);
+    }
+
+    /**
+     * Returns whether the user type is a {@link UserManager#USER_TYPE_PROFILE_CLONE clone user}.
+     * @hide
+     */
+    public static boolean isUserTypeCloneProfile(@Nullable String userType) {
+        return USER_TYPE_PROFILE_CLONE.equals(userType);
+    }
+
+    /**
+     * @hide
+     * @deprecated Use {@link #isRestrictedProfile()}
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.CREATE_USERS}
+    )
+    public boolean isLinkedUser() {
+        return isRestrictedProfile();
+    }
+
+    /**
+     * Used to check if the context user is a restricted profile. Restricted profiles
+     * may have a reduced number of available apps, app restrictions, and account restrictions.
+     *
+     * @return whether the context user is a restricted profile.
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.CREATE_USERS}
+    )
+    public boolean isRestrictedProfile() {
+        try {
+            return mService.isRestricted(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check if a user is a restricted profile. Restricted profiles may have a reduced number of
+     * available apps, app restrictions, and account restrictions.
+     *
+     * @param user the user to check
+     * @return whether the user is a restricted profile.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS},
+            conditional = true)
+    public boolean isRestrictedProfile(@NonNull UserHandle user) {
+        try {
+            return mService.isRestricted(user.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the calling context user can have a restricted profile.
+     * @return whether the context user can have a restricted profile.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware
+    public boolean canHaveRestrictedProfile() {
+        try {
+            return mService.canHaveRestrictedProfile(mUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the context user has at least one restricted profile associated with it.
+     * @return whether the user has a restricted profile associated with it
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public boolean hasRestrictedProfiles() {
+        try {
+            return mService.hasRestrictedProfiles(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the parent of a restricted profile.
+     *
+     * @return the parent of the user or {@code null} if the user is not restricted profile
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    @UserHandleAware
+    public @Nullable UserHandle getRestrictedProfileParent() {
+        final UserInfo info = getUserInfo(mUserId);
+        if (info == null) return null;
+        if (!info.isRestricted()) return null;
+        final int parent = info.restrictedProfileParentId;
+        if (parent == UserHandle.USER_NULL) return null;
+        return UserHandle.of(parent);
+    }
+
+    /**
+     * Checks if a user is a guest user.
+     * @return whether user is a guest user.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    public boolean isGuestUser(@UserIdInt int userId) {
+        UserInfo user = getUserInfo(userId);
+        return user != null && user.isGuest();
+    }
+
+    /**
+     * Used to check if the context user is a guest user. A guest user may be transient.
+     *
+     * @return whether the context user is a guest user.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public boolean isGuestUser() {
+        UserInfo user = getUserInfo(getContextUserIfAppropriate());
+        return user != null && user.isGuest();
+    }
+
+
+    /**
+     * Checks if the context user is a demo user. When running in a demo user,
+     * apps can be more helpful to the user, or explain their features in more detail.
+     *
+     * @return whether the context user is a demo user.
+     */
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresPermissionIfNotCaller = android.Manifest.permission.MANAGE_USERS
+    )
+    public boolean isDemoUser() {
+        try {
+            return mService.isDemoUser(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the calling context user is running in a profile. A profile is a user that
+     * typically has its own separate data but shares its UI with some parent user. For example, a
+     * {@link #isManagedProfile() managed profile} is a type of profile.
+     *
+     * @return whether the caller is in a profile.
+     */
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public boolean isProfile() {
+        return isProfile(mUserId);
+    }
+
+    private boolean isProfile(@UserIdInt int userId) {
+        final String profileType = getProfileType(userId);
+        return profileType != null && !profileType.equals("");
+    }
+
+    /**
+     * Returns the user type of the context user if it is a profile.
+     *
+     * This is a more specific form of {@link #getUserType()} with relaxed permission requirements.
+     *
+     * @return the user type of the context user if it is a {@link #isProfile() profile},
+     *         an empty string if it is not a profile,
+     *         or null if the user doesn't exist.
+     */
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    private @Nullable String getProfileType() {
+        return getProfileType(mUserId);
+    }
+
+    /** @see #getProfileType() */
+    private @Nullable String getProfileType(@UserIdInt int userId) {
+        // First, the typical case (i.e. the *process* user, not necessarily the context user).
+        // This cache cannot be become invalidated since it's about the calling process itself.
+        if (userId == UserHandle.myUserId()) {
+            // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
+            // Worst case we might end up calling the AIDL method multiple times but that's fine.
+            if (mProfileTypeOfProcessUser != null) {
+                return mProfileTypeOfProcessUser;
+            }
+            try {
+                final String profileType = mService.getProfileType(userId);
+                if (profileType != null) {
+                    return mProfileTypeOfProcessUser = profileType.intern();
+                }
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+
+        // The userId is not for the process's user. Use a slower cache that handles invalidation.
+        return mProfileTypeCache.query(userId);
+    }
+
+    /**
+     * Checks if the context user is a managed profile.
+     *
+     * Note that this applies specifically to <em>managed</em> profiles. For profiles in general,
+     * use {@link #isProfile()} instead.
+     *
+     * @return whether the context user is a managed profile.
+     */
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public boolean isManagedProfile() {
+        return isManagedProfile(getContextUserIfAppropriate());
+    }
+
+    /**
+     * Checks if the specified user is a managed profile.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS} permission, otherwise the caller
+     * must be in the same profile group of specified user.
+     *
+     * Note that this applies specifically to <em>managed</em> profiles. For profiles in general,
+     * use {@link #isProfile()} instead.
+     *
+     * @return whether the specified user is a managed profile.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isManagedProfile(@UserIdInt int userId) {
+        return isUserTypeManagedProfile(getProfileType(userId));
+    }
+
+    /**
+     * Checks if the context user is a clone profile.
+     *
+     * @return whether the context user is a clone profile.
+     *
+     * @see android.os.UserManager#USER_TYPE_PROFILE_CLONE
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    @SuppressAutoDoc
+    public boolean isCloneProfile() {
+        return isUserTypeCloneProfile(getProfileType());
+    }
+
+    /**
+     * Checks if the context user is an ephemeral user.
+     *
+     * @return whether the context user is an ephemeral user.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    @UserHandleAware
+    public boolean isEphemeralUser() {
+        return isUserEphemeral(mUserId);
+    }
+
+    /**
+     * Returns whether the specified user is ephemeral.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    public boolean isUserEphemeral(@UserIdInt int userId) {
+        final UserInfo user = getUserInfo(userId);
+        return user != null && user.isEphemeral();
+    }
+
+    /**
+     * Return whether the given user is actively running.  This means that
+     * the user is in the "started" state, not "stopped" -- it is currently
+     * allowed to run code through scheduled alarms, receiving broadcasts,
+     * etc.  A started user may be either the current foreground user or a
+     * background user; the result here does not distinguish between the two.
+     *
+     * <p>Note prior to Android Nougat MR1 (SDK version <= 24;
+     * {@link android.os.Build.VERSION_CODES#N}, this API required a system permission
+     * in order to check other profile's status.
+     * Since Android Nougat MR1 (SDK version >= 25;
+     * {@link android.os.Build.VERSION_CODES#N_MR1}), the restriction has been relaxed, and now
+     * it'll accept any {@link android.os.UserHandle} within the same profile group as the caller.
+     *
+     * @param user The user to retrieve the running state for.
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isUserRunning(UserHandle user) {
+        return isUserRunning(user.getIdentifier());
+    }
+
+    /** @hide */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isUserRunning(@UserIdInt int userId) {
+        try {
+            return mService.isUserRunning(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return whether the given user is actively running <em>or</em> stopping.
+     * This is like {@link #isUserRunning(UserHandle)}, but will also return
+     * true if the user had been running but is in the process of being stopped
+     * (but is not yet fully stopped, and still running some code).
+     *
+     * <p>Note prior to Android Nougat MR1 (SDK version <= 24;
+     * {@link android.os.Build.VERSION_CODES#N}, this API required a system permission
+     * in order to check other profile's status.
+     * Since Android Nougat MR1 (SDK version >= 25;
+     * {@link android.os.Build.VERSION_CODES#N_MR1}), the restriction has been relaxed, and now
+     * it'll accept any {@link android.os.UserHandle} within the same profile group as the caller.
+     *
+     * @param user The user to retrieve the running state for.
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isUserRunningOrStopping(UserHandle user) {
+        try {
+            // TODO: reconcile stopped vs stopping?
+            return ActivityManager.getService().isUserRunning(
+                    user.getIdentifier(), ActivityManager.FLAG_OR_STOPPED);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the context user is running in the foreground.
+     *
+     * @return whether the context user is running in the foreground.
+     */
+    @UserHandleAware
+    public boolean isUserForeground() {
+        try {
+            return mService.isUserForeground(mUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isVisibleBackgroundUsersEnabled() {
+        return SystemProperties.getBoolean("fw.visible_bg_users",
+                Resources.getSystem()
+                        .getBoolean(R.bool.config_multiuserVisibleBackgroundUsers));
+    }
+
+    /**
+     * Returns whether the device allows (full) users to be started in background visible in a given
+     * display (which would allow them to launch activities in that display).
+     *
+     * @return {@code false} for most devices, except on automotive builds for vehiches with
+     * passenger displays.
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean isVisibleBackgroundUsersSupported() {
+        return isVisibleBackgroundUsersEnabled();
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isVisibleBackgroundUsersOnDefaultDisplayEnabled() {
+        return SystemProperties.getBoolean("fw.visible_bg_users_on_default_display",
+                Resources.getSystem()
+                        .getBoolean(R.bool.config_multiuserVisibleBackgroundUsersOnDefaultDisplay));
+    }
+
+    /**
+     * Returns whether the device allows (full) users to be started in background visible in the
+     * {@link android.view.Display#DEFAULT_DISPLAY default display}.
+     *
+     * @return {@code false} for most devices, except passenger-only automotive build (i.e., when
+     * Android runs in a separate system in the back seat to manage the passenger displays).
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean isVisibleBackgroundUsersOnDefaultDisplaySupported() {
+        return isVisibleBackgroundUsersOnDefaultDisplayEnabled();
+    }
+
+    /**
+     * Checks if the user is visible at the moment.
+     *
+     * <p>Roughly speaking, a "visible user" is a user that can present UI on at least one display.
+     * It includes:
+     *
+     * <ol>
+     *   <li>The current foreground user.
+     *   <li>(Running) profiles of the current foreground user.
+     *   <li>Background users assigned to secondary displays (for example, passenger users on
+     *   automotive builds, using the display associated with their seats).
+     * </ol>
+     *
+     * @return whether the user is visible at the moment, as defined above.
+     *
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.MANAGE_USERS"
+    })
+    public boolean isUserVisible() {
+        try {
+            return mService.isUserVisible(mUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the visible users (as defined by {@link #isUserVisible()}.
+     *
+     * @return visible users at the moment.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.MANAGE_USERS"
+    })
+    public @NonNull Set<UserHandle> getVisibleUsers() {
+        ArraySet<UserHandle> result = new ArraySet<>();
+        try {
+            int[] visibleUserIds = mService.getVisibleUsers();
+            if (visibleUserIds != null) {
+                for (int userId : visibleUserIds) {
+                    result.add(UserHandle.of(userId));
+                }
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+        return result;
+    }
+
+    /**
+     * See {@link com.android.server.pm.UserManagerInternal#getMainDisplayAssignedToUser(int)}.
+     *
+     * @hide
+     */
+    @TestApi
+    public int getMainDisplayIdAssignedToUser() {
+        try {
+            return mService.getMainDisplayIdAssignedToUser();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return whether the context user is running in an "unlocked" state.
+     * <p>
+     * On devices with direct boot, a user is unlocked only after they've
+     * entered their credentials (such as a lock pattern or PIN). On devices
+     * without direct boot, a user is unlocked as soon as it starts.
+     * <p>
+     * When a user is locked, only device-protected data storage is available.
+     * When a user is unlocked, both device-protected and credential-protected
+     * private app data storage is available.
+     *
+     * @see Intent#ACTION_USER_UNLOCKED
+     * @see Context#createDeviceProtectedStorageContext()
+     */
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS}
+    )
+    public boolean isUserUnlocked() {
+        return isUserUnlocked(getContextUserIfAppropriate());
+    }
+
+    /**
+     * Return whether the given user is running in an "unlocked" state.
+     * <p>
+     * On devices with direct boot, a user is unlocked only after they've
+     * entered their credentials (such as a lock pattern or PIN). On devices
+     * without direct boot, a user is unlocked as soon as it starts.
+     * <p>
+     * When a user is locked, only device-protected data storage is available.
+     * When a user is unlocked, both device-protected and credential-protected
+     * private app data storage is available.
+     * <p>Requires {@code android.permission.MANAGE_USERS} or
+     * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+     * must be the calling user or a profile associated with it.
+     *
+     * @param user to retrieve the unlocked state for.
+     * @see Intent#ACTION_USER_UNLOCKED
+     * @see Context#createDeviceProtectedStorageContext()
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isUserUnlocked(UserHandle user) {
+        return isUserUnlocked(user.getIdentifier());
+    }
+
+    private static final String CACHE_KEY_IS_USER_UNLOCKED_PROPERTY =
+            "cache_key.is_user_unlocked";
+
+    private final PropertyInvalidatedCache<Integer, Boolean> mIsUserUnlockedCache =
+            new PropertyInvalidatedCache<Integer, Boolean>(
+                32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
+                @Override
+                public Boolean recompute(Integer query) {
+                    try {
+                        return mService.isUserUnlocked(query);
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+                @Override
+                public boolean bypass(Integer query) {
+                    return query < 0;
+                }
+            };
+
+    // Uses IS_USER_UNLOCKED_PROPERTY for invalidation as the APIs have the same dependencies.
+    private final PropertyInvalidatedCache<Integer, Boolean> mIsUserUnlockingOrUnlockedCache =
+            new PropertyInvalidatedCache<Integer, Boolean>(
+                32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
+                @Override
+                public Boolean recompute(Integer query) {
+                    try {
+                        return mService.isUserUnlockingOrUnlocked(query);
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+                @Override
+                public boolean bypass(Integer query) {
+                    return query < 0;
+                }
+            };
+
+    /** @hide */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isUserUnlocked(@UserIdInt int userId) {
+        return mIsUserUnlockedCache.query(userId);
+    }
+
+    /** @hide */
+    public void disableIsUserUnlockedCache() {
+        mIsUserUnlockedCache.disableLocal();
+        mIsUserUnlockingOrUnlockedCache.disableLocal();
+    }
+
+    /** @hide */
+    public static final void invalidateIsUserUnlockedCache() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_IS_USER_UNLOCKED_PROPERTY);
+    }
+
+    /**
+     * Return whether the provided user is already running in an
+     * "unlocked" state or in the process of unlocking.
+     * <p>
+     * On devices with direct boot, a user is unlocked only after they've
+     * entered their credentials (such as a lock pattern or PIN). On devices
+     * without direct boot, a user is unlocked as soon as it starts.
+     * <p>
+     * When a user is locked, only device-protected data storage is available.
+     * When a user is unlocked, both device-protected and credential-protected
+     * private app data storage is available.
+     *
+     * <p>Requires {@code android.permission.MANAGE_USERS} or
+     * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+     * must be the calling user or a profile associated with it.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isUserUnlockingOrUnlocked(@NonNull UserHandle user) {
+        return isUserUnlockingOrUnlocked(user.getIdentifier());
+    }
+
+    /** @hide */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
+        return mIsUserUnlockingOrUnlockedCache.query(userId);
+    }
+
+    /**
+     * Return the time when the calling user started in elapsed milliseconds since boot,
+     * or 0 if not started.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    // NOT @UserHandleAware
+    public long getUserStartRealtime() {
+        if (getContextUserIfAppropriate() != UserHandle.myUserId()) {
+            // Note: If we want to support this in the future, also annotate with
+            //       @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+            throw new IllegalArgumentException("Calling from a context differing from the calling "
+                    + "user is not currently supported.");
+        }
+        try {
+            return mService.getUserStartRealtime();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the time when the context user was unlocked elapsed milliseconds since boot,
+     * or 0 if not unlocked.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    // NOT @UserHandleAware
+    public long getUserUnlockRealtime() {
+        if (getContextUserIfAppropriate() != UserHandle.myUserId()) {
+            // Note: If we want to support this in the future, also annotate with
+            //       @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+            throw new IllegalArgumentException("Calling from a context differing from the calling "
+                    + "user is not currently supported.");
+        }
+        try {
+            return mService.getUserUnlockRealtime();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the UserInfo object describing a specific user.
+     * @param userId the user handle of the user whose information is being requested.
+     * @return the UserInfo object for a specific user.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
+    public UserInfo getUserInfo(@UserIdInt int userId) {
+        try {
+            return mService.getUserInfo(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a {@link UserProperties} object describing the properties of the given user.
+     *
+     * Note that the caller may not have permission to access all items; requesting any item for
+     * which permission is lacking will throw a {@link SecurityException}.
+     *
+     * <p> Requires
+     * {@code android.Manifest.permission#MANAGE_USERS},
+     * {@code android.Manifest.permission#QUERY_USERS}, or
+     * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}
+     * permission, or else the caller must be in the same profile group as the caller.
+     *
+     * @param userHandle the user handle of the user whose information is being requested.
+     * @return a UserProperties object for a specific user.
+     * @throws IllegalArgumentException if {@code userHandle} doesn't correspond to an existing user
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public @NonNull UserProperties getUserProperties(@NonNull UserHandle userHandle) {
+        return mUserPropertiesCache.query(userHandle.getIdentifier());
+    }
+
+    /**
+     * @hide
+     *
+     * Returns who set a user restriction on a user.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return The source of user restriction. Any combination of {@link #RESTRICTION_NOT_SET},
+     *         {@link #RESTRICTION_SOURCE_SYSTEM}, {@link #RESTRICTION_SOURCE_DEVICE_OWNER}
+     *         and {@link #RESTRICTION_SOURCE_PROFILE_OWNER}
+     * @deprecated use {@link #getUserRestrictionSources(String, int)} instead.
+     */
+    @Deprecated
+    @SystemApi
+    @UserRestrictionSource
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.QUERY_USERS})
+    public int getUserRestrictionSource(@UserRestrictionKey String restrictionKey,
+            UserHandle userHandle) {
+        try {
+            return mService.getUserRestrictionSource(restrictionKey, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Returns a list of users who set a user restriction on a given user.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return a list of user ids enforcing this restriction.
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS})
+    public List<EnforcingUser> getUserRestrictionSources(
+            @UserRestrictionKey String restrictionKey, UserHandle userHandle) {
+        try {
+            return mService.getUserRestrictionSources(restrictionKey, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the user-wide restrictions imposed on the context user.
+     * @return a Bundle containing all the restrictions.
+     */
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS}
+    )
+    public Bundle getUserRestrictions() {
+        try {
+            return mService.getUserRestrictions(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the user-wide restrictions imposed on the user specified by <code>userHandle</code>.
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return a Bundle containing all the restrictions.
+     *
+     * <p>Requires {@code android.permission.MANAGE_USERS} or
+     * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+     * must be the calling user or a profile associated with it.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public Bundle getUserRestrictions(UserHandle userHandle) {
+        try {
+            return mService.getUserRestrictions(userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+     /**
+     * @hide
+     * Returns whether the given user has been disallowed from performing certain actions
+     * or setting certain settings through UserManager (e.g. this type of restriction would prevent
+     * the guest user from doing certain things, such as making calls). This method disregards
+     * restrictions set by device policy.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     */
+    @TestApi
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public boolean hasBaseUserRestriction(@UserRestrictionKey @NonNull String restrictionKey,
+            @NonNull UserHandle userHandle) {
+        try {
+            return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This will no longer work.  Device owners and profile owners should use
+     * {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
+     */
+    // System apps should use UserManager.setUserRestriction() instead.
+    @Deprecated
+    public void setUserRestrictions(Bundle restrictions) {
+        throw new UnsupportedOperationException("This method is no longer supported");
+    }
+
+    /**
+     * This will no longer work.  Device owners and profile owners should use
+     * {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
+     */
+    // System apps should use UserManager.setUserRestriction() instead.
+    @Deprecated
+    public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
+        throw new UnsupportedOperationException("This method is no longer supported");
+    }
+
+    /**
+     * Sets the value of a specific restriction on the context user.
+     * Requires the MANAGE_USERS permission.
+     * @param key the key of the restriction
+     * @param value the value for the restriction
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
+     */
+    @Deprecated
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public void setUserRestriction(String key, boolean value) {
+        try {
+            mService.setUserRestriction(key, value, getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Sets the value of a specific restriction on a specific user.
+     * @param key the key of the restriction
+     * @param value the value for the restriction
+     * @param userHandle the user whose restriction is to be changed.
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
+     */
+    @Deprecated
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
+    public void setUserRestriction(String key, boolean value, UserHandle userHandle) {
+        try {
+            mService.setUserRestriction(key, value, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the context user has been disallowed from performing certain actions
+     * or setting certain settings.
+     *
+     * @param restrictionKey The string key representing the restriction.
+     * @return {@code true} if the context user has the given restriction, {@code false} otherwise.
+     */
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS}
+    )
+    public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey) {
+        return hasUserRestrictionForUser(restrictionKey, getContextUserIfAppropriate());
+    }
+
+    /**
+     * @hide
+     * Returns whether the given user has been disallowed from performing certain actions
+     * or setting certain settings.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @deprecated Use {@link #hasUserRestrictionForUser(String, UserHandle)} instead.
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    @Deprecated
+    public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey,
+            UserHandle userHandle) {
+        return hasUserRestrictionForUser(restrictionKey, userHandle);
+    }
+
+    /**
+     * Returns whether the given user has been disallowed from performing certain actions
+     * or setting certain settings.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     *
+     * <p>Requires {@code android.permission.MANAGE_USERS} or
+     * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+     * must be the calling user or a profile associated with it.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey,
+            @NonNull UserHandle userHandle) {
+        return hasUserRestrictionForUser(restrictionKey, userHandle.getIdentifier());
+    }
+
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    private boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey,
+            @UserIdInt int userId) {
+        try {
+            return mService.hasUserRestriction(restrictionKey, userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Returns whether any user on the device has the given user restriction set.
+     */
+    public boolean hasUserRestrictionOnAnyUser(@UserRestrictionKey String restrictionKey) {
+        try {
+            return mService.hasUserRestrictionOnAnyUser(restrictionKey);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Checks whether changing the given setting to the given value is prohibited
+     * by the corresponding user restriction in the given user.
+     *
+     * May only be called by the OS itself.
+     *
+     * @return {@code true} if the change is prohibited, {@code false} if the change is allowed.
+     */
+    public boolean isSettingRestrictedForUser(String setting, @UserIdInt int userId,
+            String value, int callingUid) {
+        try {
+            return mService.isSettingRestrictedForUser(setting, userId, value, callingUid);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Register a binder callback for user restrictions changes.
+     * May only be called by the OS itself.
+     */
+    public void addUserRestrictionsListener(final IUserRestrictionsListener listener) {
+        try {
+            mService.addUserRestrictionsListener(listener);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the serial number for a user.  This is a device-unique
+     * number assigned to that user; if the user is deleted and then a new
+     * user created, the new users will not be given the same serial number.
+     * @param user The user whose serial number is to be retrieved.
+     * @return The serial number of the given user; returns -1 if the
+     * given UserHandle does not exist.
+     * @see #getUserForSerialNumber(long)
+     */
+    public long getSerialNumberForUser(UserHandle user) {
+        return getUserSerialNumber(user.getIdentifier());
+    }
+
+    /**
+     * Return the user associated with a serial number previously
+     * returned by {@link #getSerialNumberForUser(UserHandle)}.
+     * @param serialNumber The serial number of the user that is being
+     * retrieved.
+     * @return Return the user associated with the serial number, or null
+     * if there is not one.
+     * @see #getSerialNumberForUser(UserHandle)
+     */
+    public UserHandle getUserForSerialNumber(long serialNumber) {
+        int ident = getUserHandle((int) serialNumber);
+        return ident >= 0 ? new UserHandle(ident) : null;
+    }
+
+    /**
+     * Creates a user with the specified name and options.
+     * Default user restrictions will be applied.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param name the user's name
+     * @param flags UserInfo flags that identify the type of user and other properties.
+     * @see UserInfo
+     *
+     * @return the UserInfo object for the created user, or null if the user could not be created.
+     * @throws IllegalArgumentException if flags do not correspond to a valid user type.
+     * @deprecated Use {@link #createUser(String, String, int)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
+        return createUser(name, UserInfo.getDefaultUserType(flags), flags);
+    }
+
+    /**
+     * Creates a user with the specified name and options.
+     * Default user restrictions will be applied.
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
+     * {@link android.Manifest.permission#CREATE_USERS} suffices if flags are in
+     * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION.
+     *
+     * @param name     the user's name
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
+     * @param flags    UserInfo flags that specify user properties.
+     * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+     *         could not be created.
+     *
+     * @see UserInfo
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    @TestApi
+    public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
+            @UserInfoFlag int flags) {
+        try {
+            return mService.createUserWithThrow(name, userType, flags);
+        } catch (ServiceSpecificException e) {
+            return null;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a user with the specified {@link NewUserRequest}.
+     *
+     * @param newUserRequest specify the user information
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public @NonNull NewUserResponse createUser(@NonNull NewUserRequest newUserRequest) {
+        try {
+            final UserHandle userHandle = mService.createUserWithAttributes(
+                    newUserRequest.getName(),
+                    newUserRequest.getUserType(),
+                    newUserRequest.getFlags(),
+                    newUserRequest.getUserIcon(),
+                    newUserRequest.getAccountName(),
+                    newUserRequest.getAccountType(),
+                    newUserRequest.getAccountOptions());
+
+            return new NewUserResponse(userHandle, USER_OPERATION_SUCCESS);
+
+        } catch (ServiceSpecificException e) {
+            Log.w(TAG, "Exception while creating user " + newUserRequest, e);
+            return new NewUserResponse(null, e.errorCode);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Pre-creates a user of the specified type. Default user restrictions will be applied.
+     *
+     * <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
+     * at the first boot, so they when the "real" user is created (for example,
+     * by {@link #createUser(String, String, int)} or {@link #createGuest(Context)}), it
+     * takes less time.
+     *
+     * <p>This method completes the majority of work necessary for user creation: it
+     * creates user data, CE and DE encryption keys, app data directories, initializes the user and
+     * grants default permissions. When pre-created users become "real" users, only then are
+     * components notified of new user creation by firing user creation broadcasts.
+     *
+     * <p>All pre-created users are removed during system upgrade.
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
+     * {@link android.Manifest.permission#CREATE_USERS} suffices if flags are in
+     * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION.
+     *
+     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
+     * @return the {@link UserInfo} object for the created user.
+     *
+     * @throws UserOperationException if the user could not be created.
+     *
+     * @deprecated Pre-created users are deprecated. This method should no longer be used, and will
+     *             be removed once all the callers are removed.
+     *
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public @NonNull UserInfo preCreateUser(@NonNull String userType)
+            throws UserOperationException {
+        Log.w(TAG, "preCreateUser(): Pre-created user is deprecated.");
+        try {
+            return mService.preCreateUserWithThrow(userType);
+        } catch (ServiceSpecificException e) {
+            throw UserOperationException.from(e);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a guest user and configures it.
+     * @param context an application context
+     * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+     *         could not be created.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public @Nullable UserInfo createGuest(Context context) {
+        try {
+            final UserInfo guest = mService.createUserWithThrow(null, USER_TYPE_FULL_GUEST, 0);
+            Settings.Secure.putStringForUser(context.getContentResolver(),
+                    Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
+
+            if (UserManager.isGuestUserAllowEphemeralStateChange()) {
+                // Mark guest as (changeably) ephemeral if REMOVE_GUEST_ON_EXIT is 1
+                // This is done so that a user via a UI controller can choose to
+                // make a guest as ephemeral or not.
+                // Settings.Global.REMOVE_GUEST_ON_EXIT holds the choice on what the guest state
+                // should be, with default being ephemeral.
+                boolean resetGuestOnExit = Settings.Global.getInt(context.getContentResolver(),
+                                             Settings.Global.REMOVE_GUEST_ON_EXIT, 1) == 1;
+
+                if (resetGuestOnExit && !guest.isEphemeral()) {
+                    setUserEphemeral(guest.id, true);
+                }
+            }
+            return guest;
+        } catch (ServiceSpecificException e) {
+            return null;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    // TODO(b/256690588): Remove this after removing its callsites.
+    /**
+     * Gets the existing guest user if it exists.  This does not include guest users that are dying.
+     * @return The existing guest user if it exists. Null otherwise.
+     * @hide
+     *
+     * @deprecated Use {@link #getGuestUsers()}
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public UserInfo findCurrentGuestUser() {
+        try {
+            final List<UserInfo> guestUsers = mService.getGuestUsers();
+            if (guestUsers.size() == 0) {
+                return null;
+            }
+            return guestUsers.get(0);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the existing guest users.  This does not include guest users that are dying.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @NonNull List<UserInfo> getGuestUsers() {
+        try {
+            return mService.getGuestUsers();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a user with the specified name and options as a profile of the context's user.
+     *
+     * @param name the user's name.
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @param disallowedPackages packages to not install for this profile.
+     *
+     * @return the {@link android.os.UserHandle} object for the created user,
+     *         or throws {@link UserOperationException} if the user could not be created
+     *         and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
+     *         (otherwise returns {@code null}).
+     *
+     * @throws UserOperationException if the user could not be created and the calling app is
+     *         targeting {@link android.os.Build.VERSION_CODES#R} or above.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    @UserHandleAware
+    public @Nullable UserHandle createProfile(@NonNull String name, @NonNull String userType,
+            @NonNull Set<String> disallowedPackages) throws UserOperationException {
+        try {
+            return mService.createProfileForUserWithThrow(name, userType, 0,
+                    mUserId, disallowedPackages.toArray(
+                            new String[disallowedPackages.size()])).getUserHandle();
+        } catch (ServiceSpecificException e) {
+            return returnNullOrThrowUserOperationException(e,
+                    mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a user with the specified name and options as a profile of another user.
+     * <p>Requires MANAGE_USERS. CREATE_USERS suffices for ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION
+     *
+     * @param name the user's name
+     * @param flags flags that identify the type of user and other properties.
+     * @param userId new user will be a profile of this user.
+     *
+     * @return the {@link UserInfo} object for the created user, or null if the user
+     *         could not be created.
+     * @throws IllegalArgumentException if flags do not correspond to a valid user type.
+     * @deprecated Use {@link #createProfileForUser(String, String, int, int)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    @Deprecated
+    public UserInfo createProfileForUser(String name, @UserInfoFlag int flags,
+            @UserIdInt int userId) {
+        return createProfileForUser(name, UserInfo.getDefaultUserType(flags), flags,
+                userId, null);
+    }
+
+    /**
+     * Creates a user with the specified name and options as a profile of another user.
+     *
+     * @param name the user's name
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @param flags UserInfo flags that specify user properties.
+     * @param userId new user will be a profile of this user.
+     *
+     * @return the {@link UserInfo} object for the created user, or null if the user
+     *         could not be created.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public @Nullable UserInfo createProfileForUser(String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId) {
+        return createProfileForUser(name, userType, flags, userId, null);
+    }
+
+    /**
+     * Version of {@link #createProfileForUser(String, String, int, int)} that allows you to specify
+     * any packages that should not be installed in the new profile by default, these packages can
+     * still be installed later by the user if needed.
+     *
+     * @param name the user's name
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @param flags UserInfo flags that specify user properties.
+     * @param userId new user will be a profile of this user.
+     * @param disallowedPackages packages that will not be installed in the profile being created.
+     *
+     * @return the {@link UserInfo} object for the created user, or {@code null} if the user could
+     *         not be created.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public @Nullable UserInfo createProfileForUser(@Nullable String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
+        try {
+            return mService.createProfileForUserWithThrow(name, userType, flags, userId,
+                    disallowedPackages);
+        } catch (ServiceSpecificException e) {
+            return null;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Similar to {@link #createProfileForUser(String, String, int, int, String[])}
+     * except bypassing the checking of {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
+     *
+     * @see #createProfileForUser(String, String, int, int, String[])
+     * @hide
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public @Nullable UserInfo createProfileForUserEvenWhenDisallowed(String name,
+            @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
+            String[] disallowedPackages) {
+        try {
+            return mService.createProfileForUserEvenWhenDisallowedWithThrow(name, userType, flags,
+                    userId, disallowedPackages);
+        } catch (ServiceSpecificException e) {
+            return null;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a restricted profile with the specified name. This method also sets necessary
+     * restrictions and adds shared accounts (with the context user).
+     *
+     * @param name profile's name
+     * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+     *         could not be created.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    @UserHandleAware
+    public @Nullable UserInfo createRestrictedProfile(@Nullable String name) {
+        try {
+            final int parentUserId = mUserId;
+            final UserInfo profile = mService.createRestrictedProfileWithThrow(name, parentUserId);
+            final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+            AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
+                    UserHandle.of(profile.id));
+            return profile;
+        } catch (ServiceSpecificException e) {
+            return null;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns an intent to create a user for the provided name and account name. The name
+     * and account name will be used when the setup process for the new user is started.
+     * <p>
+     * The intent should be launched using startActivityForResult and the return result will
+     * indicate if the user consented to adding a new user and if the operation succeeded. Any
+     * errors in creating the user will be returned in the result code. If the user cancels the
+     * request, the return result will be {@link Activity#RESULT_CANCELED}. On success, the
+     * result code will be {@link Activity#RESULT_OK}.
+     * <p>
+     * Use {@link #supportsMultipleUsers()} to first check if the device supports this operation
+     * at all.
+     * <p>
+     * The new user is created but not initialized. After switching into the user for the first
+     * time, the preferred user name and account information are used by the setup process for that
+     * user.
+     *
+     * This API should only be called if the current user is an {@link #isAdminUser() admin} user,
+     * as otherwise the returned intent will not be able to create a user.
+     *
+     * @param userName Optional name to assign to the user.
+     * @param accountName Optional account name that will be used by the setup wizard to initialize
+     *                    the user.
+     * @param accountType Optional account type for the account to be created. This is required
+     *                    if the account name is specified.
+     * @param accountOptions Optional bundle of data to be passed in during account creation in the
+     *                       new user via {@link AccountManager#addAccount(String, String, String[],
+     *                       Bundle, android.app.Activity, android.accounts.AccountManagerCallback,
+     *                       Handler)}.
+     * @return An Intent that can be launched from an Activity.
+     * @see #USER_CREATION_FAILED_NOT_PERMITTED
+     * @see #USER_CREATION_FAILED_NO_MORE_USERS
+     * @see #supportsMultipleUsers
+     */
+    public static Intent createUserCreationIntent(@Nullable String userName,
+            @Nullable String accountName,
+            @Nullable String accountType, @Nullable PersistableBundle accountOptions) {
+        Intent intent = new Intent(ACTION_CREATE_USER);
+        if (userName != null) {
+            intent.putExtra(EXTRA_USER_NAME, userName);
+        }
+        if (accountName != null && accountType == null) {
+            throw new IllegalArgumentException("accountType must be specified if accountName is "
+                    + "specified");
+        }
+        if (accountName != null) {
+            intent.putExtra(EXTRA_USER_ACCOUNT_NAME, accountName);
+        }
+        if (accountType != null) {
+            intent.putExtra(EXTRA_USER_ACCOUNT_TYPE, accountType);
+        }
+        if (accountOptions != null) {
+            intent.putExtra(EXTRA_USER_ACCOUNT_OPTIONS, accountOptions);
+        }
+        return intent;
+    }
+
+    /**
+     * Returns the list of the system packages that would be installed on this type of user upon
+     * its creation.
+     *
+     * Returns {@code null} if all system packages would be installed.
+     *
+     * @hide
+     */
+    @TestApi
+    @SuppressLint("NullableCollection")
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public @Nullable Set<String> getPreInstallableSystemPackages(@NonNull String userType) {
+        try {
+            final String[] installableSystemPackages
+                    = mService.getPreInstallableSystemPackages(userType);
+            if (installableSystemPackages == null) {
+                return null;
+            }
+            return new ArraySet<>(installableSystemPackages);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Returns the preferred account name for the context user's creation.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public String getSeedAccountName() {
+        try {
+            return mService.getSeedAccountName(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Returns the preferred account type for the context user's creation.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public String getSeedAccountType() {
+        try {
+            return mService.getSeedAccountType(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Returns the preferred account's options bundle for user creation.
+     * @return Any options set by the requestor that created the context user.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public PersistableBundle getSeedAccountOptions() {
+        try {
+            return mService.getSeedAccountOptions(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Called by a system activity to set the seed account information of a user created
+     * through the user creation intent.
+     * @param userId
+     * @param accountName
+     * @param accountType
+     * @param accountOptions
+     * @see #createUserCreationIntent(String, String, String, PersistableBundle)
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void setSeedAccountData(int userId, String accountName, String accountType,
+            PersistableBundle accountOptions) {
+        try {
+            mService.setSeedAccountData(userId, accountName, accountType, accountOptions,
+                    /* persist= */ true);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Clears the seed information used to create the context user.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public void clearSeedAccountData() {
+        try {
+            mService.clearSeedAccountData(getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Marks the guest user for deletion to allow a new guest to be created before deleting
+     * the current user who is a guest.
+     * @param userId
+     * @return
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean markGuestForDeletion(@UserIdInt int userId) {
+        try {
+            return mService.markGuestForDeletion(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the user as enabled, if such an user exists.
+     *
+     * <p>Note that the default is true, it's only that managed profiles might not be enabled.
+     * Also ephemeral users can be disabled to indicate that their removal is in progress and they
+     * shouldn't be re-entered. Therefore ephemeral users should not be re-enabled once disabled.
+     *
+     * @param userId the id of the profile to enable
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void setUserEnabled(@UserIdInt int userId) {
+        try {
+            mService.setUserEnabled(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Assigns admin privileges to the user, if such a user exists.
+     *
+     * <p>Note that this does not alter the user's pre-existing user restrictions.
+     *
+     * @param userId the id of the user to become admin
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.MANAGE_USERS
+    })
+    public void setUserAdmin(@UserIdInt int userId) {
+        try {
+            mService.setUserAdmin(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Revokes admin privileges from the user, if such a user exists.
+     *
+     * <p>Note that this does not alter the user's pre-existing user restrictions.
+     *
+     * @param userId the id of the user to revoke admin rights from
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.MANAGE_USERS
+    })
+    public void revokeUserAdmin(@UserIdInt int userId) {
+        try {
+            mService.revokeUserAdmin(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Evicts the user's credential encryption key from memory by stopping and restarting the user.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void evictCredentialEncryptionKey(@UserIdInt int userId) {
+        try {
+            mService.evictCredentialEncryptionKey(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the number of users currently created on the device.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public int getUserCount() {
+        List<UserInfo> users = getUsers();
+        return users != null ? users.size() : 1;
+    }
+
+    /**
+     * Returns information for all fully-created users on this device, including ones marked for
+     * deletion.
+     *
+     * <p>To retrieve only users that are not marked for deletion, use {@link #getAliveUsers()}.
+     *
+     * <p>To retrieve *all* users (including partial and pre-created users), use
+     * {@link #getUsers(boolean, boolean, boolean)) getUsers(false, false, false)}.
+     *
+     * <p>To retrieve a more specific list of users, use
+     * {@link #getUsers(boolean, boolean, boolean)}.
+     *
+     * @return the list of users that were created.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    @TestApi
+    public @NonNull List<UserInfo> getUsers() {
+        return getUsers(/*excludePartial= */ true, /* excludeDying= */ false,
+                /* excludePreCreated= */ true);
+    }
+
+    /**
+     * Returns information for all "usable" users on this device (i.e, it excludes users that are
+     * marked for deletion, pre-created users, etc...).
+     *
+     * <p>To retrieve all fully-created users, use {@link #getUsers()}.
+     *
+     * <p>To retrieve a more specific list of users, use
+     * {@link #getUsers(boolean, boolean, boolean)}.
+     *
+     * @return the list of users that were created.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    @TestApi
+    public @NonNull List<UserInfo> getAliveUsers() {
+        return getUsers(/*excludePartial= */ true, /* excludeDying= */ true,
+                /* excludePreCreated= */ true);
+    }
+
+    /**
+     * @deprecated use {@link #getAliveUsers()} for {@code getUsers(true)}, or
+     * {@link #getUsers()} for @code getUsers(false)}.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
+        return getUsers(/*excludePartial= */ true, excludeDying,
+                /* excludePreCreated= */ true);
+    }
+
+    /**
+     * Returns information for all users on this device, based on the filtering parameters.
+     *
+     * @deprecated Pre-created users are deprecated and no longer supported.
+     *             Use {@link #getUsers()}, or {@link #getAliveUsers()} instead.
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+            boolean excludePreCreated) {
+        try {
+            return mService.getUsers(excludePartial, excludeDying, excludePreCreated);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the user handles for all users on this device, based on the filtering parameters.
+     *
+     * @param excludeDying specify if the list should exclude users being removed.
+     * @return the list of user handles.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) {
+        List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
+                /* excludePreCreated= */ true);
+        List<UserHandle> result = new ArrayList<>(users.size());
+        for (UserInfo user : users) {
+            result.add(user.getUserHandle());
+        }
+        return result;
+    }
+
+    /**
+     * Returns serial numbers of all users on this device.
+     *
+     * @param excludeDying specify if the list should exclude users being removed.
+     * @return the list of serial numbers of users that exist on the device.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public long[] getSerialNumbersOfUsers(boolean excludeDying) {
+        List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
+                /* excludePreCreated= */ true);
+        long[] result = new long[users.size()];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = users.get(i).serialNumber;
+        }
+        return result;
+    }
+
+    /**
+     * @return the user's account name, null if not found.
+     * @hide
+     */
+    @RequiresPermission( allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.MANAGE_USERS
+    })
+    public @Nullable String getUserAccount(@UserIdInt int userId) {
+        try {
+            return mService.getUserAccount(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set account name for the given user.
+     * @hide
+     */
+    @RequiresPermission( allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.MANAGE_USERS
+    })
+    public void setUserAccount(@UserIdInt int userId, @Nullable String accountName) {
+        try {
+            mService.setUserAccount(userId, accountName);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns information for Primary user (which in practice is the same as the System user).
+     *
+     * @return the Primary user, null if not found.
+     * @deprecated For the system user, call {@link #getUserInfo} on {@link UserHandle#USER_SYSTEM},
+     *             or just use {@link UserHandle#SYSTEM} or {@link UserHandle#USER_SYSTEM}.
+     *             For the designated MainUser, use {@link #getMainUser()}.
+     *
+     * @hide
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @Nullable UserInfo getPrimaryUser() {
+        try {
+            return mService.getPrimaryUser();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the user who was last in the foreground, not including the current user and not
+     * including profiles.
+     *
+     * <p>Returns {@code null} if there is no previous user, for example if there
+     * is only one full user (i.e. only one user which is not a profile) on the device.
+     *
+     * <p>This method may be used for example to find the user to switch back to if the
+     * current user is removed, or if creating a new user is aborted.
+     *
+     * <p>Note that reboots do not interrupt this calculation; the previous user need not have
+     * used the device since it rebooted.
+     *
+     * <p>Note also that on devices that support multiple users on multiple displays, it is possible
+     * that the returned user will be visible on a secondary display, as the foreground user is the
+     * one associated with the main display.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
+    public @Nullable UserHandle getPreviousForegroundUser() {
+        try {
+            final int previousUser = mService.getPreviousFullUserToEnterForeground();
+            if (previousUser == UserHandle.USER_NULL) {
+                return null;
+            }
+            return UserHandle.of(previousUser);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks whether it's possible to add more users.
+     *
+     * @return true if more users can be added, false if limit has been reached.
+     *
+     * @deprecated use {@link #canAddMoreUsers(String)} instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public boolean canAddMoreUsers() {
+        // TODO(b/142482943): UMS has different logic, excluding Demo and Profile from counting. Why
+        //                    not here? The logic is inconsistent. See UMS.canAddMoreManagedProfiles
+        final List<UserInfo> users = getAliveUsers();
+        final int totalUserCount = users.size();
+        int aliveUserCount = 0;
+        for (int i = 0; i < totalUserCount; i++) {
+            UserInfo user = users.get(i);
+            if (!user.isGuest()) {
+                aliveUserCount++;
+            }
+        }
+        return aliveUserCount < getMaxSupportedUsers();
+    }
+
+    /**
+     * Checks whether it is possible to add more users of the given user type.
+     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+     * @return true if more users of the given type can be added, otherwise false.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public boolean canAddMoreUsers(@NonNull String userType) {
+        try {
+            if (userType.equals(USER_TYPE_FULL_GUEST)) {
+                return mService.canAddMoreUsersOfType(userType);
+            } else {
+                return canAddMoreUsers() && mService.canAddMoreUsersOfType(userType);
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the remaining number of users of the given type that can be created.
+     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+     * @return how many additional users can be created.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
+    public int getRemainingCreatableUserCount(@NonNull String userType) {
+        Objects.requireNonNull(userType, "userType must not be null");
+        try {
+            return mService.getRemainingCreatableUserCount(userType);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the remaining number of profiles that can be added to the context user.
+     * <p>Note that is applicable to any profile type (currently not including Restricted profiles).
+     *
+     * @param userType the type of profile, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @return how many additional profiles can be created.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
+    @UserHandleAware
+    public int getRemainingCreatableProfileCount(@NonNull String userType) {
+        Objects.requireNonNull(userType, "userType must not be null");
+        try {
+            // TODO(b/142482943): Perhaps let the following code apply to restricted users too.
+            return mService.getRemainingCreatableProfileCount(userType, mUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks whether it's possible to add more managed profiles.
+     * if allowedToRemoveOne is true and if the user already has a managed profile, then return if
+     * we could add a new managed profile to this user after removing the existing one.
+     *
+     * @return true if more managed profiles can be added, false if limit has been reached.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
+    public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
+        try {
+            return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks whether it's possible to add more profiles of the given type to the given user.
+     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @return true if more profiles can be added, false if limit has been reached.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
+    public boolean canAddMoreProfilesToUser(@NonNull String userType, @UserIdInt int userId) {
+        try {
+            return mService.canAddMoreProfilesToUser(userType, userId, false);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks whether this device supports users of the given user type.
+     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+     * @return true if the creation of users of the given user type is enabled on this device.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public boolean isUserTypeEnabled(@NonNull String userType) {
+        try {
+            return mService.isUserTypeEnabled(userType);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns list of the profiles of userId including userId itself.
+     * Note that this returns both enabled and not enabled profiles. See
+     * {@link #getEnabledProfiles(int)} if you need only the enabled ones.
+     * <p>Note that this includes all profile types (not including Restricted profiles).
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#CREATE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS} if userId is not the calling user.
+     * @param userId profiles of this user will be returned.
+     * @return the list of profiles.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
+    public List<UserInfo> getProfiles(@UserIdInt int userId) {
+        try {
+            return mService.getProfiles(userId, false /* enabledOnly */);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the 2 provided user handles belong to the same profile group.
+     *
+     * @param user one of the two user handles to check.
+     * @param otherUser one of the two user handles to check.
+     * @return true if the two users are in the same profile group.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS})
+    public boolean isSameProfileGroup(@NonNull UserHandle user, @NonNull UserHandle otherUser) {
+        return isSameProfileGroup(user.getIdentifier(), otherUser.getIdentifier());
+    }
+
+    /**
+     * Checks if the 2 provided user ids belong to the same profile group.
+     * @param userId one of the two user ids to check.
+     * @param otherUserId one of the two user ids to check.
+     * @return true if the two user ids are in the same profile group.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS})
+    public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) {
+        try {
+            return mService.isSameProfileGroup(userId, otherUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns list of the profiles of userId including userId itself.
+     * Note that this returns only enabled.
+     * <p>Note that this includes all profile types (not including Restricted profiles).
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#CREATE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS} if userId is not the calling user.
+     * @param userId profiles of this user will be returned.
+     * @return the list of profiles.
+     * @deprecated use {@link #getUserProfiles()} instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
+    public List<UserInfo> getEnabledProfiles(@UserIdInt int userId) {
+        try {
+            return mService.getProfiles(userId, true /* enabledOnly */);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a list of UserHandles for profiles associated with the context user, including the
+     * user itself.
+     * <p>Note that this includes all profile types (not including Restricted profiles).
+     *
+     * @return A non-empty list of UserHandles associated with the context user.
+     */
+    @UserHandleAware(
+            enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+            requiresAnyOfPermissionsIfNotCaller = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
+    public List<UserHandle> getUserProfiles() {
+        int[] userIds = getProfileIds(getContextUserIfAppropriate(), true /* enabledOnly */);
+        return convertUserIdsToUserHandles(userIds);
+    }
+
+    /**
+     * Returns a list of ids for enabled profiles associated with the context user including the
+     * user itself.
+     * <p>Note that this includes all profile types (not including Restricted profiles).
+     *
+     * @return A non-empty list of UserHandles associated with the context user.
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
+    public @NonNull List<UserHandle> getEnabledProfiles() {
+        return getProfiles(true);
+    }
+
+    /**
+     * Returns a list of ids for all profiles associated with the context user including the user
+     * itself.
+     * <p>Note that this includes all profile types (not including Restricted profiles).
+     *
+     * @return A non-empty list of UserHandles associated with the context user.
+     * @hide
+     */
+    @SystemApi
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
+    public @NonNull List<UserHandle> getAllProfiles() {
+        return getProfiles(false);
+    }
+
+    /**
+     * Returns a list of ids for profiles associated with the context user including the user
+     * itself.
+     * <p>Note that this includes all profile types (not including Restricted profiles).
+     *
+     * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
+     * @return A non-empty list of UserHandles associated with the context user.
+     */
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
+    private @NonNull List<UserHandle> getProfiles(boolean enabledOnly) {
+        final int[] userIds = getProfileIds(mUserId, enabledOnly);
+        return convertUserIdsToUserHandles(userIds);
+    }
+
+    /** Converts the given array of userIds to a List of UserHandles. */
+    private @NonNull List<UserHandle> convertUserIdsToUserHandles(@NonNull int[] userIds) {
+        final List<UserHandle> result = new ArrayList<>(userIds.length);
+        for (int userId : userIds) {
+            result.add(UserHandle.of(userId));
+        }
+        return result;
+    }
+
+    /**
+     * Returns a list of ids for profiles associated with the specified user including the user
+     * itself.
+     * <p>Note that this includes all profile types (not including Restricted profiles).
+     *
+     * @param userId      id of the user to return profiles for
+     * @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles
+     * @return A non-empty list of ids of profiles associated with the specified user.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
+    public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
+        try {
+            return mService.getProfileIds(userId, enabledOnly);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @see #getProfileIds(int, boolean)
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
+    public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
+        return getProfileIds(userId, false /* enabledOnly */);
+    }
+
+    /**
+     * @see #getProfileIds(int, boolean)
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
+    public int[] getEnabledProfileIds(@UserIdInt int userId) {
+        return getProfileIds(userId, true /* enabledOnly */);
+    }
+
+    /**
+     * Returns the device credential owner id of the profile from
+     * which this method is called, or userId if called from a user that
+     * is not a profile.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
+    public int getCredentialOwnerProfile(@UserIdInt int userId) {
+        try {
+            return mService.getCredentialOwnerProfile(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the parent of the profile which this method is called from
+     * or null if called from a user that is not a profile.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS
+    })
+    public UserInfo getProfileParent(@UserIdInt int userId) {
+        try {
+            return mService.getProfileParent(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the parent of a user profile.
+     *
+     * @param user the handle of the user profile
+     *
+     * @return the parent of the user or {@code null} if the user is not profile
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS
+    })
+    public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
+        UserInfo info = getProfileParent(user.getIdentifier());
+
+        if (info == null) {
+            return null;
+        }
+
+        return UserHandle.of(info.id);
+    }
+
+    /**
+     * Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
+     * managed profile don't run, generate notifications, or consume data or battery.
+     * <p>
+     * If a user's credential is needed to turn off quiet mode, a confirm credential screen will be
+     * shown to the user.
+     * <p>
+     * The change may not happen instantly, however apps can listen for
+     * {@link Intent#ACTION_MANAGED_PROFILE_AVAILABLE} and
+     * {@link Intent#ACTION_MANAGED_PROFILE_UNAVAILABLE} broadcasts in order to be notified of
+     * the change of the quiet mode. Apps can also check the current state of quiet mode by
+     * calling {@link #isQuietModeEnabled(UserHandle)}.
+     * <p>
+     * The caller must either be the foreground default launcher or have one of these permissions:
+     * {@code MANAGE_USERS} or {@code MODIFY_QUIET_MODE}.
+     *
+     * @param enableQuietMode whether quiet mode should be enabled or disabled
+     * @param userHandle user handle of the profile
+     * @return {@code false} if user's credential is needed in order to turn off quiet mode,
+     *         {@code true} otherwise
+     * @throws SecurityException if the caller is invalid
+     * @throws IllegalArgumentException if {@code userHandle} is not a managed profile
+     *
+     * @see #isQuietModeEnabled(UserHandle)
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            Manifest.permission.MODIFY_QUIET_MODE}, conditional = true)
+    public boolean requestQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle) {
+        return requestQuietModeEnabled(enableQuietMode, userHandle, null);
+    }
+
+    /**
+     * Perform the same operation as {@link #requestQuietModeEnabled(boolean, UserHandle)}, but
+     * with a flag to tweak the behavior of the request.
+     *
+     * @param enableQuietMode whether quiet mode should be enabled or disabled
+     * @param userHandle user handle of the profile
+     * @param flags Can be 0 or {@link #QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED}.
+     * @return {@code false} if user's credential is needed in order to turn off quiet mode,
+     *         {@code true} otherwise
+     * @throws SecurityException if the caller is invalid
+     * @throws IllegalArgumentException if {@code userHandle} is not a managed profile
+     *
+     * @see #isQuietModeEnabled(UserHandle)
+     */
+    public boolean requestQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle,
+            @QuietModeFlag int flags) {
+        return requestQuietModeEnabled(enableQuietMode, userHandle, null, flags);
+    }
+
+    /**
+     * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
+     * a target to start when user is unlocked. If {@code target} is specified, caller must have
+     * the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @see {@link #requestQuietModeEnabled(boolean, UserHandle)}
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean requestQuietModeEnabled(
+            boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target) {
+        return requestQuietModeEnabled(enableQuietMode, userHandle, target, 0);
+    }
+
+    /**
+     * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
+     * a target to start when user is unlocked. If {@code target} is specified, caller must have
+     * the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @see #requestQuietModeEnabled(boolean, UserHandle)
+     * @hide
+     */
+    public boolean requestQuietModeEnabled(
+            boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target,
+            int flags) {
+        try {
+            return mService.requestQuietModeEnabled(
+                    mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target,
+                    flags);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the given profile is in quiet mode or not.
+     * Notes: Quiet mode is only supported for managed profiles.
+     *
+     * @param userHandle The user handle of the profile to be queried.
+     * @return true if the profile is in quiet mode, false otherwise.
+     */
+    public boolean isQuietModeEnabled(UserHandle userHandle) {
+        try {
+            return mService.isQuietModeEnabled(userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the given user has a badge (generally to put on profiles' icons).
+     *
+     * @param userId userId of the user in question
+     * @return true if the user's icons should display a badge; false otherwise.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public boolean hasBadge(@UserIdInt int userId) {
+        if (!isProfile(userId)) {
+            // Since currently only profiles actually have badges, we can do this optimization.
+            return false;
+        }
+        try {
+            return mService.hasBadge(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the user associated with the context has a badge (generally to put on
+     * profiles' icons).
+     *
+     * @return true if the user's icons should display a badge; false otherwise.
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    @UserHandleAware
+    public boolean hasBadge() {
+        return hasBadge(mUserId);
+    }
+
+    /**
+     * Returns the light theme badge color for the given user (generally to color a profile's
+     * icon's badge).
+     *
+     * <p>To check whether a badge color is expected for the user, first call {@link #hasBadge}.
+     *
+     * @return the color (not the resource ID) to be used for the user's badge
+     * @throws Resources.NotFoundException if no valid badge color exists for this user
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @ColorInt int getUserBadgeColor(@UserIdInt int userId) {
+        try {
+            final int resourceId = mService.getUserBadgeColorResId(userId);
+            return Resources.getSystem().getColor(resourceId, null);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the dark theme badge color for the given user (generally to color a profile's icon's
+     * badge).
+     *
+     * <p>To check whether a badge color is expected for the user, first call {@link #hasBadge}.
+     *
+     * @return the color (not the resource ID) to be used for the user's badge
+     * @throws Resources.NotFoundException if no valid badge color exists for this user
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @ColorInt int getUserBadgeDarkColor(@UserIdInt int userId) {
+        try {
+            final int resourceId = mService.getUserBadgeDarkColorResId(userId);
+            return Resources.getSystem().getColor(resourceId, null);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the Resource ID of the user's icon badge.
+     *
+     * @return the Resource ID of the user's icon badge if it has one; otherwise
+     *         {@link Resources#ID_NULL}.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
+        try {
+            return mService.getUserIconBadgeResId(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the Resource ID of the user's badge.
+     *
+     * @return the Resource ID of the user's badge if it has one; otherwise
+     *         {@link Resources#ID_NULL}.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
+        try {
+            return mService.getUserBadgeResId(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the Resource ID of the user's badge without a background.
+     *
+     * @return the Resource ID of the user's no-background badge if it has one; otherwise
+     *         {@link Resources#ID_NULL}.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
+        try {
+            return mService.getUserBadgeNoBackgroundResId(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * If the target user is a profile of the calling user or the caller
+     * is itself a profile, then this returns a badged copy of the given
+     * icon to be able to distinguish it from the original icon. For badging an
+     * arbitrary drawable use {@link #getBadgedDrawableForUser(
+     * android.graphics.drawable.Drawable, UserHandle, android.graphics.Rect, int)}.
+     * <p>
+     * If the original drawable is a BitmapDrawable and the backing bitmap is
+     * mutable as per {@link android.graphics.Bitmap#isMutable()}, the badging
+     * is performed in place and the original drawable is returned.
+     * </p>
+     *
+     * @param icon The icon to badge.
+     * @param user The target user.
+     * @return A drawable that combines the original icon and a badge as
+     *         determined by the system.
+     * @removed
+     */
+    public Drawable getBadgedIconForUser(Drawable icon, UserHandle user) {
+        return mContext.getPackageManager().getUserBadgedIcon(icon, user);
+    }
+
+    /**
+     * If the target user is a profile of the calling user or the caller
+     * is itself a profile, then this returns a badged copy of the given
+     * drawable allowing the user to distinguish it from the original drawable.
+     * The caller can specify the location in the bounds of the drawable to be
+     * badged where the badge should be applied as well as the density of the
+     * badge to be used.
+     * <p>
+     * If the original drawable is a BitmapDrawable and the backing bitmap is
+     * mutable as per {@link android.graphics.Bitmap#isMutable()}, the badging
+     * is performed in place and the original drawable is returned.
+     * </p>
+     *
+     * @param badgedDrawable The drawable to badge.
+     * @param user The target user.
+     * @param badgeLocation Where in the bounds of the badged drawable to place
+     *         the badge. If it's {@code null}, the badge is applied on top of the entire
+     *         drawable being badged.
+     * @param badgeDensity The optional desired density for the badge as per
+     *         {@link android.util.DisplayMetrics#densityDpi}. If it's not positive,
+     *         the density of the display is used.
+     * @return A drawable that combines the original drawable and a badge as
+     *         determined by the system.
+     * @removed
+     */
+    public Drawable getBadgedDrawableForUser(Drawable badgedDrawable, UserHandle user,
+            Rect badgeLocation, int badgeDensity) {
+        return mContext.getPackageManager().getUserBadgedDrawableForDensity(badgedDrawable, user,
+                badgeLocation, badgeDensity);
+    }
+
+    /**
+     * If the target user is a profile of the calling user or the caller
+     * is itself a profile, then this returns a copy of the label with
+     * badging for accessibility services like talkback. E.g. passing in "Email"
+     * and it might return "Work Email" for Email in the work profile.
+     *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+     * must be in the same profile group of specified user.
+     *
+     * @param label The label to change.
+     * @param user The target user.
+     * @return A label that combines the original label and a badge as
+     *         determined by the system.
+     * @removed
+     */
+    public CharSequence getBadgedLabelForUser(CharSequence label, UserHandle user) {
+        final int userId = user.getIdentifier();
+        if (!hasBadge(userId)) {
+            return label;
+        }
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        return dpm.getResources().getString(
+                getUpdatableUserBadgedLabelId(userId),
+                () -> getDefaultUserBadgedLabel(label, userId),
+                /* formatArgs= */ label);
+    }
+
+    private String getUpdatableUserBadgedLabelId(int userId) {
+        return isManagedProfile(userId) ? WORK_PROFILE_BADGED_LABEL : UNDEFINED;
+    }
+
+    private String getDefaultUserBadgedLabel(CharSequence label, int userId) {
+        try {
+            final int resourceId = mService.getUserBadgeLabelResId(userId);
+            return Resources.getSystem().getString(resourceId, label);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * If the user is a {@link UserManager#isProfile profile}, checks if the user
+     * shares media with its parent user (the user that created this profile).
+     * Returns false for any other type of user.
+     *
+     * @return true if the user shares media with its parent user, false otherwise.
+     *
+     * @deprecated use {@link #getUserProperties(UserHandle)} with
+     *            {@link UserProperties#isMediaSharedWithParent()} instead.
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.QUERY_USERS,
+                    Manifest.permission.INTERACT_ACROSS_USERS})
+    @SuppressAutoDoc
+    public boolean isMediaSharedWithParent() {
+        try {
+            return getUserProperties(UserHandle.of(mUserId)).isMediaSharedWithParent();
+        } catch (IllegalArgumentException e) {
+            // If the user doesn't exist, return false (for historical reasons)
+            return false;
+        }
+    }
+
+    /**
+     * Returns whether the user can have shared lockscreen credential with its parent user.
+     *
+     * This API only works for {@link UserManager#isProfile() profiles}
+     * and will always return false for any other user type.
+     *
+     * @deprecated use {@link #getUserProperties(UserHandle)} with
+     *            {@link UserProperties#isCredentialShareableWithParent()} instead.
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.QUERY_USERS,
+                    Manifest.permission.INTERACT_ACROSS_USERS})
+    @SuppressAutoDoc
+    public boolean isCredentialSharableWithParent() {
+        try {
+            return getUserProperties(UserHandle.of(mUserId)).isCredentialShareableWithParent();
+        } catch (IllegalArgumentException e) {
+            // If the user doesn't exist, return false (for historical reasons)
+            return false;
+        }
+    }
+
+    /**
+     * Removes a user and its profiles along with their associated data.
+     * @param userId the integer handle of the user.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public boolean removeUser(@UserIdInt int userId) {
+        try {
+            return mService.removeUser(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes a user and its profiles along with their associated data.
+     *
+     * @param user the user that needs to be removed.
+     * @return {@code true} if the user was successfully removed, {@code false} otherwise.
+     * @throws IllegalArgumentException if {@code user} is {@code null}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public boolean removeUser(@NonNull UserHandle user) {
+        if (user == null) {
+            throw new IllegalArgumentException("user cannot be null");
+        }
+        return removeUser(user.getIdentifier());
+    }
+
+
+    /**
+     * Similar to {@link #removeUser(int)} except bypassing the checking of
+     * {@link UserManager#DISALLOW_REMOVE_USER}
+     * or {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE}.
+     *
+     * @see {@link #removeUser(int)}
+     * @hide
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
+        try {
+            return mService.removeUserEvenWhenDisallowed(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Immediately removes the user and its profiles or, if the user cannot be removed, such as
+     * when the user is the current user, then set the user as ephemeral
+     * so that it will be removed when it is stopped.
+     *
+     * @param overrideDevicePolicy when {@code true}, user is removed even if the caller has
+     * the {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
+     *
+     * @return the {@link RemoveResult} code: {@link #REMOVE_RESULT_REMOVED},
+     * {@link #REMOVE_RESULT_DEFERRED}, {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED},
+     * {@link #REMOVE_RESULT_ERROR_USER_RESTRICTION}, {@link #REMOVE_RESULT_ERROR_USER_NOT_FOUND},
+     * {@link #REMOVE_RESULT_ERROR_SYSTEM_USER},
+     * {@link #REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN}, or
+     * {@link #REMOVE_RESULT_ERROR_UNKNOWN}. All error codes have negative values.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public @RemoveResult int removeUserWhenPossible(@NonNull UserHandle user,
+            boolean overrideDevicePolicy) {
+        try {
+            return mService.removeUserWhenPossible(user.getIdentifier(), overrideDevicePolicy);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check if {@link #removeUserWhenPossible} returned a success code which means that the user
+     * has been removed or is slated for removal.
+     *
+     * @param result is {@link #RemoveResult} code return by {@link #removeUserWhenPossible}.
+     * @return {@code true} if it is a success code.
+     * @hide
+     */
+    @SystemApi
+    public static boolean isRemoveResultSuccessful(@RemoveResult int result) {
+        return result >= 0;
+    }
+
+    /**
+     * Updates the user's name.
+     *
+     * @param userId the user's integer id
+     * @param name the new name for the user
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void setUserName(@UserIdInt int userId, String name) {
+        try {
+            mService.setUserName(userId, name);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the user as ephemeral or non-ephemeral.
+     *
+     * If the user was initially created as ephemeral then this
+     * method has no effect and false is returned.
+     *
+     * @param userId the user's integer id
+     * @param enableEphemeral true: change user state to ephemeral,
+     *                        false: change user state to non-ephemeral
+     * @return true: user now has the desired ephemeral state,
+     *         false: desired user ephemeral state could not be set
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS})
+    public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) {
+        try {
+            return mService.setUserEphemeral(userId, enableEphemeral);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Updates the context user's name.
+     *
+     * @param name the new name for the user
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware
+    public void setUserName(@Nullable String name) {
+        setUserName(mUserId, name);
+    }
+
+    /**
+     * Sets the user's photo.
+     * @param userId the user for whom to change the photo.
+     * @param icon the bitmap to set as the photo.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void setUserIcon(@UserIdInt int userId, @NonNull Bitmap icon) {
+        try {
+            mService.setUserIcon(userId, icon);
+        } catch (ServiceSpecificException e) {
+            return;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the context user's photo.
+     *
+     * @param icon the bitmap to set as the photo.
+     *
+     * @throws UserOperationException according to the function signature, but may not actually
+     * throw it in practice. Catch RuntimeException instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @UserHandleAware
+    public void setUserIcon(@NonNull Bitmap icon) throws UserOperationException {
+        setUserIcon(mUserId, icon);
+    }
+
+    /**
+     * Returns a bitmap of the user's photo
+     * @param userId the user whose photo we want to read.
+     * @return a {@link Bitmap} of the user's photo, or null if there's no photo.
+     * @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+    public Bitmap getUserIcon(@UserIdInt int userId) {
+        try {
+            ParcelFileDescriptor fd = mService.getUserIcon(userId);
+            if (fd != null) {
+                try {
+                    return BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
+                } finally {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+        return null;
+    }
+
+    /**
+     * Returns a Bitmap for the context user's photo.
+     *
+     * @return a {@link Bitmap} of the user's photo, or null if there's no photo.
+     * @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+    @UserHandleAware
+    public @Nullable Bitmap getUserIcon() {
+        return getUserIcon(mUserId);
+    }
+
+    /**
+     * Returns the maximum number of users that can be created on this device. A return value
+     * of 1 means that it is a single user device.
+     * @hide
+     * @return a value greater than or equal to 1
+     */
+    @UnsupportedAppUsage
+    public static int getMaxSupportedUsers() {
+        // Don't allow multiple users on certain builds
+        if (android.os.Build.ID.startsWith("JVP")) return 1;
+        return Math.max(1, SystemProperties.getInt("fw.max_users",
+                Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers)));
+    }
+
+    /**
+     * Returns true if the user switcher is enabled (regardless of whether there is anything
+     * interesting for it to show).
+     *
+     * @return true if user switcher is enabled
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS // And INTERACT_ if diff profile group
+    })
+    @UserHandleAware
+    public boolean isUserSwitcherEnabled() {
+        return isUserSwitcherEnabled(true);
+    }
+
+    /**
+     * Returns true if the user switcher should be shown.
+     *
+     * @param showEvenIfNotActionable value to return if the feature is enabled but there is nothing
+     *                                actionable for the user to do anyway
+     * @return true if user switcher should be shown.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS // And INTERACT_ if diff profile group
+    })
+    @UserHandleAware
+    public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable) {
+
+        try {
+            return mService.isUserSwitcherEnabled(showEvenIfNotActionable, mUserId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static boolean isDeviceInDemoMode(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.DEVICE_DEMO_MODE, 0) > 0;
+    }
+
+    /**
+     * Returns a serial number on this device for a given userId. User handles can be recycled
+     * when deleting and creating users, but serial numbers are not reused until the device is wiped.
+     * @param userId
+     * @return a serial number associated with that user, or -1 if the userId is not valid.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getUserSerialNumber(@UserIdInt int userId) {
+        try {
+            return mService.getUserSerialNumber(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a userId on this device for a given user serial number. User handles can be
+     * recycled when deleting and creating users, but serial numbers are not reused until the device
+     * is wiped.
+     * @param userSerialNumber
+     * @return the userId associated with that user serial number, or -1 if the serial number
+     * is not valid.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @UserIdInt int getUserHandle(int userSerialNumber) {
+        try {
+            return mService.getUserHandle(userSerialNumber);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a {@link Bundle} containing any saved application restrictions for the context user,
+     * for the given package name. Only an application with this package name can call this method.
+     *
+     * <p>The returned {@link Bundle} consists of key-value pairs, as defined by the application,
+     * where the types of values may be:
+     * <ul>
+     * <li>{@code boolean}
+     * <li>{@code int}
+     * <li>{@code String} or {@code String[]}
+     * <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]}
+     * </ul>
+     *
+     * <p>NOTE: The method performs disk I/O and shouldn't be called on the main thread
+     *
+     * @param packageName the package name of the calling application
+     * @return a {@link Bundle} with the restrictions for that package, or an empty {@link Bundle}
+     * if there are no saved restrictions.
+     *
+     * @see #KEY_RESTRICTIONS_PENDING
+     *
+     * <p>Starting from Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+     * it is possible for there to be multiple managing apps on the device with the ability to set
+     * restrictions, e.g. an Enterprise Device Policy Controller (DPC) and a Supervision admin.
+     * This API will only to return the restrictions set by the DPCs. To retrieve restrictions
+     * set by all managing apps, use
+     * {@link android.content.RestrictionsManager#getApplicationRestrictionsPerAdmin} instead.
+     *
+     * @see DevicePolicyManager
+     */
+    @WorkerThread
+    @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public Bundle getApplicationRestrictions(String packageName) {
+        try {
+            return mService.getApplicationRestrictionsForUser(packageName,
+                    getContextUserIfAppropriate());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * <p>Starting from Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+     * it is possible for there to be multiple managing apps on the device with the ability to set
+     * restrictions, e.g. an Enterprise Device Policy Controller (DPC) and a Supervision admin.
+     * This API will only to return the restrictions set by the DPCs. To retrieve restrictions
+     * set by all managing apps, use
+     * {@link android.content.RestrictionsManager#getApplicationRestrictionsPerAdmin} instead.
+     *
+     * @hide
+     */
+    @WorkerThread
+    public Bundle getApplicationRestrictions(String packageName, UserHandle user) {
+        try {
+            return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @WorkerThread
+    public void setApplicationRestrictions(String packageName, Bundle restrictions,
+            UserHandle user) {
+        try {
+            mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets a new challenge PIN for restrictions. This is only for use by pre-installed
+     * apps and requires the MANAGE_USERS permission.
+     * @param newPin the PIN to use for challenge dialogs.
+     * @return Returns true if the challenge PIN was set successfully.
+     * @deprecated The restrictions PIN functionality is no longer provided by the system.
+     * This method is preserved for backwards compatibility reasons and always returns false.
+     */
+    @Deprecated
+    public boolean setRestrictionsChallenge(String newPin) {
+        return false;
+    }
+
+    /**
+     * @hide
+     * Set restrictions that should apply to any future guest user that's created.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void setDefaultGuestRestrictions(Bundle restrictions) {
+        try {
+            mService.setDefaultGuestRestrictions(restrictions);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Gets the default guest restrictions.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public Bundle getDefaultGuestRestrictions() {
+        try {
+            return mService.getDefaultGuestRestrictions();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns creation time of the given user. The given user must be the calling user or
+     * a profile associated with it.
+     * @param userHandle user handle of the calling user or a profile associated with the
+     *                   calling user.
+     * @return creation time in milliseconds since Epoch time.
+     */
+    public long getUserCreationTime(UserHandle userHandle) {
+        try {
+            return mService.getUserCreationTime(userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if any uninitialized user has the specific seed account name and type.
+     *
+     * @param accountName The account name to check for
+     * @param accountType The account type of the account to check for
+     * @return whether the seed account was found
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean someUserHasSeedAccount(String accountName, String accountType) {
+        try {
+            return mService.someUserHasSeedAccount(accountName, accountType);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if any initialized or uninitialized user has the specific account name and type.
+     *
+     * @param accountName The account name to check for
+     * @param accountType The account type of the account to check for
+     * @return whether the account was found
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public boolean someUserHasAccount(
+            @NonNull String accountName, @NonNull String accountType) {
+        Objects.requireNonNull(accountName, "accountName must not be null");
+        Objects.requireNonNull(accountType, "accountType must not be null");
+
+        try {
+            return mService.someUserHasAccount(accountName, accountType);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the user who should be in the foreground when boot completes. This should be called
+     * during boot, and the provided user must be a full user (i.e. not a profile).
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public void setBootUser(@NonNull UserHandle bootUser) {
+        try {
+            mService.setBootUser(bootUser.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the user who should be in the foreground when boot completes.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    @SuppressWarnings("[AndroidFrameworkContextUserId]")
+    public @NonNull UserHandle getBootUser() {
+        try {
+            return UserHandle.of(mService.getBootUser());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /* Cache key for anything that assumes that userIds cannot be re-used without rebooting. */
+    private static final String CACHE_KEY_STATIC_USER_PROPERTIES = "cache_key.static_user_props";
+
+    private final PropertyInvalidatedCache<Integer, String> mProfileTypeCache =
+            new PropertyInvalidatedCache<Integer, String>(32, CACHE_KEY_STATIC_USER_PROPERTIES) {
+                @Override
+                public String recompute(Integer query) {
+                    try {
+                        // Will be null (and not cached) if invalid user; otherwise cache the type.
+                        String profileType = mService.getProfileType(query);
+                        if (profileType != null) profileType = profileType.intern();
+                        return profileType;
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+                @Override
+                public boolean bypass(Integer query) {
+                    return query < 0;
+                }
+            };
+
+    /** @hide */
+    public static final void invalidateStaticUserProperties() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES);
+    }
+
+    /* Cache key for UserProperties object. */
+    private static final String CACHE_KEY_USER_PROPERTIES = "cache_key.user_properties";
+
+    // TODO: It would be better to somehow have this as static, so that it can work cross-context.
+    private final PropertyInvalidatedCache<Integer, UserProperties> mUserPropertiesCache =
+            new PropertyInvalidatedCache<Integer, UserProperties>(16, CACHE_KEY_USER_PROPERTIES) {
+                @Override
+                public UserProperties recompute(Integer userId) {
+                    try {
+                        // If the userId doesn't exist, this will throw rather than cache garbage.
+                        return mService.getUserPropertiesCopy(userId);
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+                @Override
+                public boolean bypass(Integer query) {
+                    return query < 0;
+                }
+            };
+
+    /** @hide */
+    public static final void invalidateUserPropertiesCache() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_USER_PROPERTIES);
+    }
+
+    /**
+     * @hide
+     * User that enforces a restriction.
+     *
+     * @see #getUserRestrictionSources(String, UserHandle)
+     */
+    @SystemApi
+    public static final class EnforcingUser implements Parcelable {
+        private final @UserIdInt int userId;
+        private final @UserRestrictionSource int userRestrictionSource;
+
+        /**
+         * @hide
+         */
+        public EnforcingUser(
+                @UserIdInt int userId, @UserRestrictionSource int userRestrictionSource) {
+            this.userId = userId;
+            this.userRestrictionSource = userRestrictionSource;
+        }
+
+        private EnforcingUser(Parcel in) {
+            userId = in.readInt();
+            userRestrictionSource = in.readInt();
+        }
+
+        public static final @android.annotation.NonNull Creator<EnforcingUser> CREATOR = new Creator<EnforcingUser>() {
+            @Override
+            public EnforcingUser createFromParcel(Parcel in) {
+                return new EnforcingUser(in);
+            }
+
+            @Override
+            public EnforcingUser[] newArray(int size) {
+                return new EnforcingUser[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(userId);
+            dest.writeInt(userRestrictionSource);
+        }
+
+        /**
+         * Returns an id of the enforcing user.
+         *
+         * <p> Will be UserHandle.USER_NULL when restriction is set by the system.
+         */
+        public UserHandle getUserHandle() {
+            return UserHandle.of(userId);
+        }
+
+        /**
+         * Returns the status of the enforcing user.
+         *
+         * <p> One of {@link #RESTRICTION_SOURCE_SYSTEM},
+         * {@link #RESTRICTION_SOURCE_DEVICE_OWNER} and
+         * {@link #RESTRICTION_SOURCE_PROFILE_OWNER}
+         */
+        public @UserRestrictionSource int getUserRestrictionSource() {
+            return userRestrictionSource;
+        }
+    }
+}
diff --git a/android-34/android/os/VibrationAttributes.java b/android-34/android/os/VibrationAttributes.java
new file mode 100644
index 0000000..b2d62eb
--- /dev/null
+++ b/android-34/android/os/VibrationAttributes.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.media.AudioAttributes;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Encapsulates a collection of attributes describing information about a vibration.
+ */
+public final class VibrationAttributes implements Parcelable {
+    private static final String TAG = "VibrationAttributes";
+
+    /** @hide */
+    @IntDef(prefix = { "USAGE_CLASS_" }, value = {
+            USAGE_CLASS_UNKNOWN,
+            USAGE_CLASS_ALARM,
+            USAGE_CLASS_FEEDBACK,
+            USAGE_CLASS_MEDIA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UsageClass {}
+
+    /** @hide */
+    @IntDef(prefix = { "USAGE_" }, value = {
+            USAGE_UNKNOWN,
+            USAGE_ACCESSIBILITY,
+            USAGE_ALARM,
+            USAGE_COMMUNICATION_REQUEST,
+            USAGE_HARDWARE_FEEDBACK,
+            USAGE_MEDIA,
+            USAGE_NOTIFICATION,
+            USAGE_PHYSICAL_EMULATION,
+            USAGE_RINGTONE,
+            USAGE_TOUCH,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Usage {}
+
+    /**
+     * Vibration usage filter value to match all usages.
+     * @hide
+     */
+    public static final int USAGE_FILTER_MATCH_ALL = -1;
+    /**
+     * Vibration usage class value to use when the vibration usage class is unknown.
+     */
+    public static final int USAGE_CLASS_UNKNOWN = 0x0;
+    /**
+     * Vibration usage class value to use when the vibration is initiated to catch user's
+     * attention, such as alarm, ringtone, and notification vibrations.
+     */
+    public static final int USAGE_CLASS_ALARM = 0x1;
+    /**
+     * Vibration usage class value to use when the vibration is initiated as a response to user's
+     * actions, such as emulation of physical effects, and texting feedback vibration.
+     */
+    public static final int USAGE_CLASS_FEEDBACK = 0x2;
+    /**
+     * Vibration usage class value to use when the vibration is part of media, such as music, movie,
+     * soundtrack, game or animations.
+     */
+    public static final int USAGE_CLASS_MEDIA = 0x3;
+
+    /**
+     * Mask for vibration usage class value.
+     */
+    public static final int USAGE_CLASS_MASK = 0xF;
+
+    /**
+     * Usage value to use when usage is unknown.
+     */
+    public static final int USAGE_UNKNOWN = 0x0 | USAGE_CLASS_UNKNOWN;
+    /**
+     * Usage value to use for alarm vibrations.
+     */
+    public static final int USAGE_ALARM = 0x10 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for ringtone vibrations.
+     */
+    public static final int USAGE_RINGTONE = 0x20 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for notification vibrations.
+     */
+    public static final int USAGE_NOTIFICATION = 0x30 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for vibrations which mean a request to enter/end a
+     * communication with the user, such as a voice prompt.
+     */
+    public static final int USAGE_COMMUNICATION_REQUEST = 0x40 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for touch vibrations.
+     *
+     * <p>Most typical haptic feedback should be classed as <em>touch</em> feedback. Examples
+     * include vibrations for tap, long press, drag and scroll.
+     */
+    public static final int USAGE_TOUCH = 0x10 | USAGE_CLASS_FEEDBACK;
+    /**
+     * Usage value to use for vibrations which emulate physical hardware reactions,
+     * such as edge squeeze.
+     *
+     * <p>Note that normal screen-touch feedback "click" effects would typically be
+     * classed as {@link #USAGE_TOUCH}, and that on-screen "physical" animations
+     * like bouncing would be {@link #USAGE_MEDIA}.
+     */
+    public static final int USAGE_PHYSICAL_EMULATION = 0x20 | USAGE_CLASS_FEEDBACK;
+    /**
+     * Usage value to use for vibrations which provide a feedback for hardware
+     * component interaction, such as a fingerprint sensor.
+     */
+    public static final int USAGE_HARDWARE_FEEDBACK = 0x30 | USAGE_CLASS_FEEDBACK;
+    /**
+     * Usage value to use for accessibility vibrations, such as with a screen reader.
+     */
+    public static final int USAGE_ACCESSIBILITY = 0x40 | USAGE_CLASS_FEEDBACK;
+    /**
+     * Usage value to use for media vibrations, such as music, movie, soundtrack, animations, games,
+     * or any interactive media that isn't for touch feedback specifically.
+     */
+    public static final int USAGE_MEDIA = 0x10 | USAGE_CLASS_MEDIA;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "FLAG_" }, flag = true, value = {
+            FLAG_BYPASS_INTERRUPTION_POLICY,
+            FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
+            FLAG_INVALIDATE_SETTINGS_CACHE,
+            FLAG_PIPELINED_EFFECT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flag{}
+
+    /**
+     * Flag requesting vibration effect to be played even under limited interruptions.
+     *
+     * <p>Only privileged apps can ignore user settings that limit interruptions, and this
+     * flag will be ignored otherwise.
+     */
+    public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 1;
+
+    /**
+     * Flag requesting vibration effect to be played even when user settings are disabling it.
+     *
+     * <p>Flag introduced to represent
+     * {@link android.view.HapticFeedbackConstants#FLAG_IGNORE_GLOBAL_SETTING} and
+     * {@link AudioAttributes#FLAG_BYPASS_MUTE}.
+     *
+     * @hide
+     */
+    public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF = 1 << 1;
+
+    /**
+     * Flag requesting vibration effect to be played with fresh user settings values.
+     *
+     * <p>This flag is not protected by any permission, but vibrations that use it require an extra
+     * query of user vibration intensity settings, ringer mode and other controls that affect the
+     * vibration effect playback, which can increase the latency for the overall request.
+     *
+     * <p>This is intended to be used on scenarios where the user settings might have changed
+     * recently, and needs to be applied to this vibration, like settings controllers that preview
+     * newly set intensities to the user.
+     *
+     * @hide
+     */
+    public static final int FLAG_INVALIDATE_SETTINGS_CACHE = 1 << 2;
+
+    /**
+     * Flag requesting that this vibration effect be pipelined with other vibration effects from the
+     * same package that also carry this flag.
+     *
+     * <p>Pipelined effects won't cancel a running pipelined effect, but will instead play after
+     * it completes. However, only one pipelined effect can be waiting at a time - so if an effect
+     * is already waiting (but not running), it will be cancelled in favor of a newer one.
+     *
+     * @hide
+     */
+    public static final int FLAG_PIPELINED_EFFECT = 1 << 3;
+
+    /**
+     * All flags supported by vibrator service, update it when adding new flag.
+     * @hide
+     */
+    public static final int FLAG_ALL_SUPPORTED =
+            FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
+                    | FLAG_INVALIDATE_SETTINGS_CACHE | FLAG_PIPELINED_EFFECT;
+
+    /** Creates a new {@link VibrationAttributes} instance with given usage. */
+    public static @NonNull VibrationAttributes createForUsage(@Usage int usage) {
+        return new VibrationAttributes.Builder().setUsage(usage).build();
+    }
+
+    private final int mUsage;
+    private final int mFlags;
+    private final int mOriginalAudioUsage;
+
+    private VibrationAttributes(@Usage int usage, @AudioAttributes.AttributeUsage int audioUsage,
+            @Flag int flags) {
+        mUsage = usage;
+        mOriginalAudioUsage = audioUsage;
+        mFlags = flags & FLAG_ALL_SUPPORTED;
+    }
+
+    /**
+     * Return the vibration usage class.
+     */
+    @UsageClass
+    public int getUsageClass() {
+        return mUsage & USAGE_CLASS_MASK;
+    }
+
+    /**
+     * Return the vibration usage.
+     */
+    @Usage
+    public int getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Return the flags.
+     * @return a combined mask of all flags
+     */
+    @Flag
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Check whether a flag is set
+     * @return true if a flag is set and false otherwise
+     */
+    public boolean isFlagSet(@Flag int flag) {
+        return (mFlags & flag) > 0;
+    }
+
+    /**
+     * Return {@link AudioAttributes} usage equivalent to {@link #getUsage()}.
+     * @return one of {@link AudioAttributes#SDK_USAGES} that represents {@link #getUsage()}
+     * @hide
+     */
+    @TestApi
+    @AudioAttributes.AttributeUsage
+    public int getAudioUsage() {
+        if (mOriginalAudioUsage != AudioAttributes.USAGE_UNKNOWN) {
+            // Return same audio usage set in the Builder.
+            return mOriginalAudioUsage;
+        }
+        // Return correct audio usage based on the vibration usage set in the Builder.
+        switch (mUsage) {
+            case USAGE_NOTIFICATION:
+                return AudioAttributes.USAGE_NOTIFICATION;
+            case USAGE_COMMUNICATION_REQUEST:
+                return AudioAttributes.USAGE_VOICE_COMMUNICATION;
+            case USAGE_RINGTONE:
+                return AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+            case USAGE_TOUCH:
+                return AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
+            case USAGE_ALARM:
+                return AudioAttributes.USAGE_ALARM;
+            case USAGE_ACCESSIBILITY:
+                return AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
+            case USAGE_MEDIA:
+                return AudioAttributes.USAGE_MEDIA;
+            default:
+                return AudioAttributes.USAGE_UNKNOWN;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mUsage);
+        dest.writeInt(mOriginalAudioUsage);
+        dest.writeInt(mFlags);
+    }
+
+    private VibrationAttributes(Parcel src) {
+        mUsage = src.readInt();
+        mOriginalAudioUsage = src.readInt();
+        mFlags = src.readInt();
+    }
+
+    public static final @NonNull Parcelable.Creator<VibrationAttributes>
+            CREATOR = new Parcelable.Creator<VibrationAttributes>() {
+                public VibrationAttributes createFromParcel(Parcel p) {
+                    return new VibrationAttributes(p);
+                }
+                public VibrationAttributes[] newArray(int size) {
+                    return new VibrationAttributes[size];
+                }
+            };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        VibrationAttributes rhs = (VibrationAttributes) o;
+        return mUsage == rhs.mUsage && mOriginalAudioUsage == rhs.mOriginalAudioUsage
+                && mFlags == rhs.mFlags;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUsage, mOriginalAudioUsage, mFlags);
+    }
+
+    @Override
+    public String toString() {
+        return "VibrationAttributes:"
+                + " Usage=" + usageToString()
+                + " Audio Usage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
+                + " Flags=" + mFlags;
+    }
+
+    /** @hide */
+    public String usageToString() {
+        return usageToString(mUsage);
+    }
+
+    /** @hide */
+    public static String usageToString(@Usage int usage) {
+        switch (usage) {
+            case USAGE_UNKNOWN:
+                return "UNKNOWN";
+            case USAGE_ALARM:
+                return "ALARM";
+            case USAGE_ACCESSIBILITY:
+                return "ACCESSIBILITY";
+            case USAGE_RINGTONE:
+                return "RINGTONE";
+            case USAGE_NOTIFICATION:
+                return "NOTIFICATION";
+            case USAGE_COMMUNICATION_REQUEST:
+                return "COMMUNICATION_REQUEST";
+            case USAGE_MEDIA:
+                return "MEDIA";
+            case USAGE_TOUCH:
+                return "TOUCH";
+            case USAGE_PHYSICAL_EMULATION:
+                return "PHYSICAL_EMULATION";
+            case USAGE_HARDWARE_FEEDBACK:
+                return "HARDWARE_FEEDBACK";
+            default:
+                return "unknown usage " + usage;
+        }
+    }
+
+    /**
+     * Builder class for {@link VibrationAttributes} objects.
+     * By default, all information is set to UNKNOWN.
+     */
+    public static final class Builder {
+        private int mUsage = USAGE_UNKNOWN;
+        private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
+        private int mFlags = 0x0;
+
+        /**
+         * Constructs a new Builder with the defaults.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given VibrationAttributes.
+         */
+        public Builder(@Nullable VibrationAttributes vib) {
+            if (vib != null) {
+                mUsage = vib.mUsage;
+                mOriginalAudioUsage = vib.mOriginalAudioUsage;
+                mFlags = vib.mFlags;
+            }
+        }
+
+        /**
+         * Constructs a new Builder from AudioAttributes.
+         */
+        public Builder(@NonNull AudioAttributes audio) {
+            setUsage(audio);
+            setFlags(audio);
+        }
+
+        private void setUsage(@NonNull AudioAttributes audio) {
+            mOriginalAudioUsage = audio.getUsage();
+            switch (audio.getUsage()) {
+                case AudioAttributes.USAGE_NOTIFICATION:
+                case AudioAttributes.USAGE_NOTIFICATION_EVENT:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+                    mUsage = USAGE_NOTIFICATION;
+                    break;
+                case AudioAttributes.USAGE_VOICE_COMMUNICATION:
+                case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
+                case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+                case AudioAttributes.USAGE_ASSISTANT:
+                    mUsage = USAGE_COMMUNICATION_REQUEST;
+                    break;
+                case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
+                    mUsage = USAGE_RINGTONE;
+                    break;
+                case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
+                    mUsage = USAGE_ACCESSIBILITY;
+                    break;
+                case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
+                    mUsage = USAGE_TOUCH;
+                    break;
+                case AudioAttributes.USAGE_ALARM:
+                    mUsage = USAGE_ALARM;
+                    break;
+                case AudioAttributes.USAGE_MEDIA:
+                case AudioAttributes.USAGE_GAME:
+                    mUsage = USAGE_MEDIA;
+                    break;
+                default:
+                    mUsage = USAGE_UNKNOWN;
+            }
+        }
+
+        private void setFlags(@NonNull AudioAttributes audio) {
+            if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
+                mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY;
+            }
+            if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_MUTE) != 0) {
+                // Muted audio stream translates to vibration usage having the value
+                // Vibrator.VIBRATION_INTENSITY_OFF set in the user setting.
+                mFlags |= FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+            }
+        }
+
+        /**
+         * Combines all of the attributes that have been set and returns a new
+         * {@link VibrationAttributes} object.
+         * @return a new {@link VibrationAttributes} object
+         */
+        public @NonNull VibrationAttributes build() {
+            VibrationAttributes ans = new VibrationAttributes(mUsage, mOriginalAudioUsage, mFlags);
+            return ans;
+        }
+
+        /**
+         * Sets the attribute describing the type of the corresponding vibration.
+         * @param usage The type of usage for the vibration
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setUsage(@Usage int usage) {
+            mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
+            mUsage = usage;
+            return this;
+        }
+
+        /**
+         * Sets only the flags specified in the bitmask, leaving the other supported flag values
+         * unchanged in the builder.
+         *
+         * @param flags Combination of flags to be set.
+         * @param mask Bit range that should be changed.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setFlags(@Flag int flags, int mask) {
+            mask &= FLAG_ALL_SUPPORTED;
+            mFlags = (mFlags & ~mask) | (flags & mask);
+            return this;
+        }
+
+        /**
+         * Set all supported flags with given combination of flags, overriding any previous values
+         * set to this builder.
+         *
+         * @param flags combination of flags to be set.
+         * @return the same Builder instance.
+         *
+         * @hide
+         */
+        public @NonNull Builder setFlags(@Flag int flags) {
+            return setFlags(flags, FLAG_ALL_SUPPORTED);
+        }
+    }
+}
diff --git a/android-34/android/os/VibrationEffect.java b/android-34/android/os/VibrationEffect.java
new file mode 100644
index 0000000..4366c28
--- /dev/null
+++ b/android-34/android/os/VibrationEffect.java
@@ -0,0 +1,1569 @@
+/*
+ * 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.os;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.hardware.vibrator.V1_0.EffectStrength;
+import android.hardware.vibrator.V1_3.Effect;
+import android.net.Uri;
+import android.os.Vibrator;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.MathUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
+ *
+ * <p>These effects may be any number of things, from single shot vibrations to complex waveforms.
+ */
+public abstract class VibrationEffect implements Parcelable {
+    // Stevens' coefficient to scale the perceived vibration intensity.
+    private static final float SCALE_GAMMA = 0.65f;
+    // If a vibration is playing for longer than 1s, it's probably not haptic feedback
+    private static final long MAX_HAPTIC_FEEDBACK_DURATION = 1000;
+    // If a vibration is playing more than 3 constants, it's probably not haptic feedback
+    private static final long MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE = 3;
+
+    /**
+     * The default vibration strength of the device.
+     */
+    public static final int DEFAULT_AMPLITUDE = -1;
+
+    /**
+     * The maximum amplitude value
+     * @hide
+     */
+    public static final int MAX_AMPLITUDE = 255;
+
+    /**
+     * A click effect. Use this effect as a baseline, as it's the most common type of click effect.
+     */
+    public static final int EFFECT_CLICK = Effect.CLICK;
+
+    /**
+     * A double click effect.
+     */
+    public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
+
+    /**
+     * A tick effect. This effect is less strong compared to {@link #EFFECT_CLICK}.
+     */
+    public static final int EFFECT_TICK = Effect.TICK;
+
+    /**
+     * A thud effect.
+     * @see #get(int)
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
+    public static final int EFFECT_THUD = Effect.THUD;
+
+    /**
+     * A pop effect.
+     * @see #get(int)
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
+    public static final int EFFECT_POP = Effect.POP;
+
+    /**
+     * A heavy click effect. This effect is stronger than {@link #EFFECT_CLICK}.
+     */
+    public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
+
+    /**
+     * A texture effect meant to replicate soft ticks.
+     *
+     * <p>Unlike normal effects, texture effects are meant to be called repeatedly, generally in
+     * response to some motion, in order to replicate the feeling of some texture underneath the
+     * user's fingers.
+     *
+     * @see #get(int)
+     * @hide
+     */
+    @TestApi
+    public static final int EFFECT_TEXTURE_TICK = Effect.TEXTURE_TICK;
+
+    /** {@hide} */
+    @TestApi
+    public static final int EFFECT_STRENGTH_LIGHT = EffectStrength.LIGHT;
+
+    /** {@hide} */
+    @TestApi
+    public static final int EFFECT_STRENGTH_MEDIUM = EffectStrength.MEDIUM;
+
+    /** {@hide} */
+    @TestApi
+    public static final int EFFECT_STRENGTH_STRONG = EffectStrength.STRONG;
+
+    /**
+     * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
+     * pattern that can be played as a ringtone with any audio, depending on the device.
+     *
+     * @see #get(Uri, Context)
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
+    public static final int[] RINGTONES = {
+        Effect.RINGTONE_1,
+        Effect.RINGTONE_2,
+        Effect.RINGTONE_3,
+        Effect.RINGTONE_4,
+        Effect.RINGTONE_5,
+        Effect.RINGTONE_6,
+        Effect.RINGTONE_7,
+        Effect.RINGTONE_8,
+        Effect.RINGTONE_9,
+        Effect.RINGTONE_10,
+        Effect.RINGTONE_11,
+        Effect.RINGTONE_12,
+        Effect.RINGTONE_13,
+        Effect.RINGTONE_14,
+        Effect.RINGTONE_15
+    };
+
+    /** @hide */
+    @IntDef(prefix = { "EFFECT_" }, value = {
+            EFFECT_TICK,
+            EFFECT_CLICK,
+            EFFECT_HEAVY_CLICK,
+            EFFECT_DOUBLE_CLICK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EffectType {}
+
+    /** @hide to prevent subclassing from outside of the framework */
+    public VibrationEffect() { }
+
+    /**
+     * Create a one shot vibration.
+     *
+     * <p>One shot vibrations will vibrate constantly for the specified period of time at the
+     * specified amplitude, and then stop.
+     *
+     * @param milliseconds The number of milliseconds to vibrate. This must be a positive number.
+     * @param amplitude The strength of the vibration. This must be a value between 1 and 255, or
+     * {@link #DEFAULT_AMPLITUDE}.
+     *
+     * @return The desired effect.
+     */
+    public static VibrationEffect createOneShot(long milliseconds, int amplitude) {
+        if (amplitude == 0) {
+            throw new IllegalArgumentException(
+                    "amplitude must either be DEFAULT_AMPLITUDE, "
+                            + "or between 1 and 255 inclusive (amplitude=" + amplitude + ")");
+        }
+        return createWaveform(new long[]{milliseconds}, new int[]{amplitude}, -1 /* repeat */);
+    }
+
+    /**
+     * Create a waveform vibration, using only off/on transitions at the provided time intervals,
+     * and potentially repeating.
+     *
+     * <p>In effect, the timings array represents the number of milliseconds <em>before</em> turning
+     * the vibrator on, followed by the number of milliseconds to keep the vibrator on, then
+     * the number of milliseconds turned off, and so on. Consequently, the first timing value will
+     * often be 0, so that the effect will start vibrating immediately.
+     *
+     * <p>This method is equivalent to calling {@link #createWaveform(long[], int[], int)} with
+     * corresponding amplitude values alternating between 0 and {@link #DEFAULT_AMPLITUDE},
+     * beginning with 0.
+     *
+     * <p>To cause the pattern to repeat, pass the index into the timings array at which to start
+     * the repetition, or -1 to disable repeating. Repeating effects will be played indefinitely
+     * and should be cancelled via {@link Vibrator#cancel()}.
+     *
+     * @param timings The pattern of alternating on-off timings, starting with an 'off' timing, and
+     *               representing the length of time to sustain the individual item (not
+     *               cumulative).
+     * @param repeat The index into the timings array at which to repeat, or -1 if you don't
+     *               want to repeat indefinitely.
+     *
+     * @return The desired effect.
+     */
+    public static VibrationEffect createWaveform(long[] timings, int repeat) {
+        int[] amplitudes = new int[timings.length];
+        for (int i = 0; i < (timings.length / 2); i++) {
+            amplitudes[i*2 + 1] = VibrationEffect.DEFAULT_AMPLITUDE;
+        }
+        return createWaveform(timings, amplitudes, repeat);
+    }
+
+    /**
+     * Computes a legacy vibration pattern (i.e. a pattern with duration values for "off/on"
+     * vibration components) that is equivalent to this VibrationEffect.
+     *
+     * <p>All non-repeating effects created with {@link #createWaveform(int[], int)} are convertible
+     * into an equivalent vibration pattern with this method. It is not guaranteed that an effect
+     * created with other means becomes converted into an equivalent legacy vibration pattern, even
+     * if it has an equivalent vibration pattern. If this method is unable to create an equivalent
+     * vibration pattern for such effects, it will return {@code null}.
+     *
+     * <p>Note that a valid equivalent long[] pattern cannot be created for an effect that has any
+     * form of repeating behavior, regardless of how the effect was created. For repeating effects,
+     * the method will always return {@code null}.
+     *
+     * @return a long array representing a vibration pattern equivalent to the VibrationEffect, if
+     *               the method successfully derived a vibration pattern equivalent to the effect
+     *               (this will always be the case if the effect was created via
+     *               {@link #createWaveform(int[], int)} and is non-repeating). Otherwise, returns
+     *               {@code null}.
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public abstract long[] computeCreateWaveformOffOnTimingsOrNull();
+
+    /**
+     * Create a waveform vibration.
+     *
+     * <p>Waveform vibrations are a potentially repeating series of timing and amplitude pairs,
+     * provided in separate arrays. For each pair, the value in the amplitude array determines
+     * the strength of the vibration and the value in the timing array determines how long it
+     * vibrates for, in milliseconds.
+     *
+     * <p>To cause the pattern to repeat, pass the index into the timings array at which to start
+     * the repetition, or -1 to disable repeating. Repeating effects will be played indefinitely
+     * and should be cancelled via {@link Vibrator#cancel()}.
+     *
+     * @param timings The timing values, in milliseconds, of the timing / amplitude pairs. Timing
+     *                values of 0 will cause the pair to be ignored.
+     * @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
+     *                   must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
+     *                   amplitude value of 0 implies the motor is off.
+     * @param repeat The index into the timings array at which to repeat, or -1 if you don't
+     *               want to repeat indefinitely.
+     *
+     * @return The desired effect.
+     */
+    public static VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
+        if (timings.length != amplitudes.length) {
+            throw new IllegalArgumentException(
+                    "timing and amplitude arrays must be of equal length"
+                            + " (timings.length=" + timings.length
+                            + ", amplitudes.length=" + amplitudes.length + ")");
+        }
+        List<StepSegment> segments = new ArrayList<>();
+        for (int i = 0; i < timings.length; i++) {
+            float parsedAmplitude = amplitudes[i] == DEFAULT_AMPLITUDE
+                    ? DEFAULT_AMPLITUDE : (float) amplitudes[i] / MAX_AMPLITUDE;
+            segments.add(new StepSegment(parsedAmplitude, /* frequencyHz= */ 0, (int) timings[i]));
+        }
+        VibrationEffect effect = new Composed(segments, repeat);
+        effect.validate();
+        return effect;
+    }
+
+    /**
+     * Create a predefined vibration effect.
+     *
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
+     * provide a better experience than you could otherwise build using the generic building
+     * blocks.
+     *
+     * <p>This will fallback to a generic pattern if one exists and there does not exist a
+     * hardware-specific implementation of the effect.
+     *
+     * @param effectId The ID of the effect to perform:
+     *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
+     *
+     * @return The desired effect.
+     */
+    @NonNull
+    public static VibrationEffect createPredefined(@EffectType int effectId) {
+        return get(effectId, true);
+    }
+
+    /**
+     * Get a predefined vibration effect.
+     *
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
+     * provide a better experience than you could otherwise build using the generic building
+     * blocks.
+     *
+     * <p>This will fallback to a generic pattern if one exists and there does not exist a
+     * hardware-specific implementation of the effect.
+     *
+     * @param effectId The ID of the effect to perform:
+     *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
+     *
+     * @return The desired effect.
+     * @hide
+     */
+    @TestApi
+    public static VibrationEffect get(int effectId) {
+        return get(effectId, true);
+    }
+
+    /**
+     * Get a predefined vibration effect.
+     *
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
+     * provide a better experience than you could otherwise build using the generic building
+     * blocks.
+     *
+     * <p>Some effects you may only want to play if there's a hardware specific implementation
+     * because they may, for example, be too disruptive to the user without tuning. The
+     * {@code fallback} parameter allows you to decide whether you want to fallback to the generic
+     * implementation or only play if there's a tuned, hardware specific one available.
+     *
+     * @param effectId The ID of the effect to perform:
+     *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
+     * @param fallback Whether to fallback to a generic pattern if a hardware specific
+     *                 implementation doesn't exist.
+     *
+     * @return The desired effect.
+     * @hide
+     */
+    @TestApi
+    public static VibrationEffect get(int effectId, boolean fallback) {
+        VibrationEffect effect = new Composed(
+                new PrebakedSegment(effectId, fallback, EffectStrength.MEDIUM));
+        effect.validate();
+        return effect;
+    }
+
+    /**
+     * Get a predefined vibration effect associated with a given URI.
+     *
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
+     * provide a better experience than you could otherwise build using the generic building
+     * blocks.
+     *
+     * @param uri The URI associated with the haptic effect.
+     * @param context The context used to get the URI to haptic effect association.
+     *
+     * @return The desired effect, or {@code null} if there's no associated effect.
+     *
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public static VibrationEffect get(Uri uri, Context context) {
+        String[] uris = context.getResources().getStringArray(
+                com.android.internal.R.array.config_ringtoneEffectUris);
+
+        // Skip doing any IPC if we don't have any effects configured.
+        if (uris.length == 0) {
+            return null;
+        }
+
+        final ContentResolver cr = context.getContentResolver();
+        Uri uncanonicalUri = cr.uncanonicalize(uri);
+        if (uncanonicalUri == null) {
+            // If we already had an uncanonical URI, it's possible we'll get null back here. In
+            // this case, just use the URI as passed in since it wasn't canonicalized in the first
+            // place.
+            uncanonicalUri = uri;
+        }
+
+        for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
+            if (uris[i] == null) {
+                continue;
+            }
+            Uri mappedUri = cr.uncanonicalize(Uri.parse(uris[i]));
+            if (mappedUri == null) {
+                continue;
+            }
+            if (mappedUri.equals(uncanonicalUri)) {
+                return get(RINGTONES[i]);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Start composing a haptic effect.
+     *
+     * @see VibrationEffect.Composition
+     */
+    @NonNull
+    public static Composition startComposition() {
+        return new Composition();
+    }
+
+    /**
+     * Start building a waveform vibration.
+     *
+     * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
+     * control over vibration amplitude and frequency via smooth transitions between values.
+     *
+     * <p>The waveform will start the first transition from the vibrator off state, with the
+     * resonant frequency by default. To provide an initial state, use
+     * {@link #startWaveform(VibrationEffect.VibrationParameter)}.
+     *
+     * @see VibrationEffect.WaveformBuilder
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static WaveformBuilder startWaveform() {
+        return new WaveformBuilder();
+    }
+
+    /**
+     * Start building a waveform vibration with an initial state specified by a
+     * {@link VibrationEffect.VibrationParameter}.
+     *
+     * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
+     * control over vibration amplitude and frequency via smooth transitions between values.
+     *
+     * @param initialParameter The initial {@link VibrationEffect.VibrationParameter} value to be
+     *                         applied at the beginning of the vibration.
+     * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+     *
+     * @see VibrationEffect.WaveformBuilder
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter) {
+        WaveformBuilder builder = startWaveform();
+        builder.addTransition(Duration.ZERO, initialParameter);
+        return builder;
+    }
+
+    /**
+     * Start building a waveform vibration with an initial state specified by two
+     * {@link VibrationEffect.VibrationParameter VibrationParameters}.
+     *
+     * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
+     * control over vibration amplitude and frequency via smooth transitions between values.
+     *
+     * @param initialParameter1 The initial {@link VibrationEffect.VibrationParameter} value to be
+     *                          applied at the beginning of the vibration.
+     * @param initialParameter2 The initial {@link VibrationEffect.VibrationParameter} value to be
+     *                          applied at the beginning of the vibration, must be a different type
+     *                          of parameter than the one specified by the first argument.
+     * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+     *
+     * @see VibrationEffect.WaveformBuilder
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter1,
+            @NonNull VibrationParameter initialParameter2) {
+        WaveformBuilder builder = startWaveform();
+        builder.addTransition(Duration.ZERO, initialParameter1, initialParameter2);
+        return builder;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    public abstract void validate();
+
+    /**
+     * Gets the estimated duration of the vibration in milliseconds.
+     *
+     * <p>For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
+     * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
+     * the length is device and potentially run-time dependent), this returns -1.
+     *
+     * @hide
+     */
+    @TestApi
+    public abstract long getDuration();
+
+    /**
+     * Checks if a given {@link Vibrator} can play this effect as intended.
+     *
+     * <p>See @link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information
+     * about what counts as supported by a vibrator, and what counts as not.
+     *
+     * @hide
+     */
+    public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+
+    /**
+     * Returns true if this effect could represent a touch haptic feedback.
+     *
+     * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
+     * for each vibration, with the correct usage. When a vibration is played with usage UNKNOWN,
+     * then this method will be used to classify the most common use case and make sure they are
+     * covered by the user settings for "Touch feedback".
+     *
+     * @hide
+     */
+    public boolean isHapticFeedbackCandidate() {
+        return false;
+    }
+
+    /**
+     * Resolve default values into integer amplitude numbers.
+     *
+     * @param defaultAmplitude the default amplitude to apply, must be between 0 and
+     *                         MAX_AMPLITUDE
+     * @return this if amplitude value is already set, or a copy of this effect with given default
+     *         amplitude otherwise
+     *
+     * @hide
+     */
+    public abstract <T extends VibrationEffect> T resolve(int defaultAmplitude);
+
+    /**
+     * Scale the vibration effect intensity with the given constraints.
+     *
+     * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+     *                    scale down the intensity, values larger than 1 will scale up
+     * @return this if there is no scaling to be done, or a copy of this effect with scaled
+     *         vibration intensity otherwise
+     *
+     * @hide
+     */
+    public abstract <T extends VibrationEffect> T scale(float scaleFactor);
+
+    /**
+     * Applies given effect strength to prebaked effects represented by one of
+     * VibrationEffect.EFFECT_*.
+     *
+     * @param effectStrength new effect strength to be applied, one of
+     *                       VibrationEffect.EFFECT_STRENGTH_*.
+     * @return this if there is no change to this effect, or a copy of this effect with applied
+     * effect strength otherwise.
+     * @hide
+     */
+    public <T extends VibrationEffect> T applyEffectStrength(int effectStrength) {
+        return (T) this;
+    }
+
+    /**
+     * Scale given vibration intensity by the given factor.
+     *
+     * @param intensity   relative intensity of the effect, must be between 0 and 1
+     * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+     *                    scale down the intensity, values larger than 1 will scale up
+     * @hide
+     */
+    public static float scale(float intensity, float scaleFactor) {
+        // Applying gamma correction to the scale factor, which is the same as encoding the input
+        // value, scaling it, then decoding the scaled value.
+        float scale = MathUtils.pow(scaleFactor, 1f / SCALE_GAMMA);
+
+        if (scaleFactor <= 1) {
+            // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
+            // Scale up requires a different curve to ensure the intensity will not become > 1.
+            return intensity * scale;
+        }
+
+        // Apply the scale factor a few more times to make the ramp curve closer to the raw scale.
+        float extraScale = MathUtils.pow(scaleFactor, 4f - scaleFactor);
+        float x = intensity * scale * extraScale;
+        float maxX = scale * extraScale; // scaled x for intensity == 1
+
+        float expX = MathUtils.exp(x);
+        float expMaxX = MathUtils.exp(maxX);
+
+        // Using f = tanh as the scale up function so the max value will converge.
+        // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
+        float a = (expMaxX + 1f) / (expMaxX - 1f);
+        float fx = (expX - 1f) / (expX + 1f);
+
+        return MathUtils.constrain(a * fx, 0f, 1f);
+    }
+
+    /** @hide */
+    public static String effectIdToString(int effectId) {
+        switch (effectId) {
+            case EFFECT_CLICK:
+                return "CLICK";
+            case EFFECT_TICK:
+                return "TICK";
+            case EFFECT_HEAVY_CLICK:
+                return "HEAVY_CLICK";
+            case EFFECT_DOUBLE_CLICK:
+                return "DOUBLE_CLICK";
+            case EFFECT_POP:
+                return "POP";
+            case EFFECT_THUD:
+                return "THUD";
+            case EFFECT_TEXTURE_TICK:
+                return "TEXTURE_TICK";
+            default:
+                return Integer.toString(effectId);
+        }
+    }
+
+    /** @hide */
+    public static String effectStrengthToString(int effectStrength) {
+        switch (effectStrength) {
+            case EFFECT_STRENGTH_LIGHT:
+                return "LIGHT";
+            case EFFECT_STRENGTH_MEDIUM:
+                return "MEDIUM";
+            case EFFECT_STRENGTH_STRONG:
+                return "STRONG";
+            default:
+                return Integer.toString(effectStrength);
+        }
+    }
+
+    /**
+     * Implementation of {@link VibrationEffect} described by a composition of one or more
+     * {@link VibrationEffectSegment}, with an optional index to represent repeating effects.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class Composed extends VibrationEffect {
+        private final ArrayList<VibrationEffectSegment> mSegments;
+        private final int mRepeatIndex;
+
+        Composed(@NonNull Parcel in) {
+            this(in.readArrayList(VibrationEffectSegment.class.getClassLoader(), android.os.vibrator.VibrationEffectSegment.class), in.readInt());
+        }
+
+        Composed(@NonNull VibrationEffectSegment segment) {
+            this(Arrays.asList(segment), /* repeatIndex= */ -1);
+        }
+
+        /** @hide */
+        public Composed(@NonNull List<? extends VibrationEffectSegment> segments, int repeatIndex) {
+            super();
+            mSegments = new ArrayList<>(segments);
+            mRepeatIndex = repeatIndex;
+        }
+
+        @NonNull
+        public List<VibrationEffectSegment> getSegments() {
+            return mSegments;
+        }
+
+        public int getRepeatIndex() {
+            return mRepeatIndex;
+        }
+
+         /** @hide */
+        @Override
+        @Nullable
+        public long[] computeCreateWaveformOffOnTimingsOrNull() {
+            if (getRepeatIndex() >= 0) {
+                // Repeating effects cannot be fully represented as a long[] legacy pattern.
+                return null;
+            }
+
+            List<VibrationEffectSegment> segments = getSegments();
+
+            // The maximum possible size of the final pattern is 1 plus the number of segments in
+            // the original effect. This is because we will add an empty "off" segment at the
+            // start of the pattern if the first segment of the original effect is an "on" segment.
+            // (because the legacy patterns start with an "off" pattern). Other than this one case,
+            // we will add the durations of back-to-back segments of similar amplitudes (amplitudes
+            // that are all "on" or "off") and create a pattern entry for the total duration, which
+            // will not take more number pattern entries than the number of segments processed.
+            long[] patternBuffer = new long[segments.size() + 1];
+            int patternIndex = 0;
+
+            for (int i = 0; i < segments.size(); i++) {
+                StepSegment stepSegment =
+                        castToValidStepSegmentForOffOnTimingsOrNull(segments.get(i));
+                if (stepSegment == null) {
+                    // This means that there is 1 or more segments of this effect that is/are not a
+                    // possible component of a legacy vibration pattern. Thus, the VibrationEffect
+                    // does not have any equivalent legacy vibration pattern.
+                    return null;
+                }
+
+                boolean isSegmentOff = stepSegment.getAmplitude() == 0;
+                // Even pattern indices are "off", and odd pattern indices are "on"
+                boolean isCurrentPatternIndexOff = (patternIndex % 2) == 0;
+                if (isSegmentOff != isCurrentPatternIndexOff) {
+                    // Move the pattern index one step ahead, so that the current segment's
+                    // "off"/"on" property matches that of the index's
+                    ++patternIndex;
+                }
+                patternBuffer[patternIndex] += stepSegment.getDuration();
+            }
+
+            return Arrays.copyOf(patternBuffer, patternIndex + 1);
+        }
+
+        /** @hide */
+        @Override
+        public void validate() {
+            int segmentCount = mSegments.size();
+            boolean hasNonZeroDuration = false;
+            for (int i = 0; i < segmentCount; i++) {
+                VibrationEffectSegment segment = mSegments.get(i);
+                segment.validate();
+                // A segment with unknown duration = -1 still counts as a non-zero duration.
+                hasNonZeroDuration |= segment.getDuration() != 0;
+            }
+            if (!hasNonZeroDuration) {
+                throw new IllegalArgumentException("at least one timing must be non-zero"
+                        + " (segments=" + mSegments + ")");
+            }
+            if (mRepeatIndex != -1) {
+                Preconditions.checkArgumentInRange(mRepeatIndex, 0, segmentCount - 1,
+                        "repeat index must be within the bounds of the segments (segments.length="
+                                + segmentCount + ", index=" + mRepeatIndex + ")");
+            }
+        }
+
+        @Override
+        public long getDuration() {
+            if (mRepeatIndex >= 0) {
+                return Long.MAX_VALUE;
+            }
+            int segmentCount = mSegments.size();
+            long totalDuration = 0;
+            for (int i = 0; i < segmentCount; i++) {
+                long segmentDuration = mSegments.get(i).getDuration();
+                if (segmentDuration < 0) {
+                    return segmentDuration;
+                }
+                totalDuration += segmentDuration;
+            }
+            return totalDuration;
+        }
+
+        /** @hide */
+        @Override
+        public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+            for (VibrationEffectSegment segment : mSegments) {
+                if (!segment.areVibrationFeaturesSupported(vibrator)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /** @hide */
+        @Override
+        public boolean isHapticFeedbackCandidate() {
+            long totalDuration = getDuration();
+            if (totalDuration > MAX_HAPTIC_FEEDBACK_DURATION) {
+                // Vibration duration is known and is longer than the max duration used to classify
+                // haptic feedbacks (or repeating indefinitely with duration == Long.MAX_VALUE).
+                return false;
+            }
+            int segmentCount = mSegments.size();
+            if (segmentCount > MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE) {
+                // Vibration has some prebaked or primitive constants, it should be limited to the
+                // max composition size used to classify haptic feedbacks.
+                return false;
+            }
+            totalDuration = 0;
+            for (int i = 0; i < segmentCount; i++) {
+                if (!mSegments.get(i).isHapticFeedbackCandidate()) {
+                    // There is at least one segment that is not a candidate for a haptic feedback.
+                    return false;
+                }
+                long segmentDuration = mSegments.get(i).getDuration();
+                if (segmentDuration > 0) {
+                    totalDuration += segmentDuration;
+                }
+            }
+            // Vibration might still have some ramp or step segments, check the known duration.
+            return totalDuration <= MAX_HAPTIC_FEEDBACK_DURATION;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public Composed resolve(int defaultAmplitude) {
+            int segmentCount = mSegments.size();
+            ArrayList<VibrationEffectSegment> resolvedSegments = new ArrayList<>(segmentCount);
+            for (int i = 0; i < segmentCount; i++) {
+                resolvedSegments.add(mSegments.get(i).resolve(defaultAmplitude));
+            }
+            if (resolvedSegments.equals(mSegments)) {
+                return this;
+            }
+            Composed resolved = new Composed(resolvedSegments, mRepeatIndex);
+            resolved.validate();
+            return resolved;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public Composed scale(float scaleFactor) {
+            int segmentCount = mSegments.size();
+            ArrayList<VibrationEffectSegment> scaledSegments = new ArrayList<>(segmentCount);
+            for (int i = 0; i < segmentCount; i++) {
+                scaledSegments.add(mSegments.get(i).scale(scaleFactor));
+            }
+            if (scaledSegments.equals(mSegments)) {
+                return this;
+            }
+            Composed scaled = new Composed(scaledSegments, mRepeatIndex);
+            scaled.validate();
+            return scaled;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public Composed applyEffectStrength(int effectStrength) {
+            int segmentCount = mSegments.size();
+            ArrayList<VibrationEffectSegment> scaledSegments = new ArrayList<>(segmentCount);
+            for (int i = 0; i < segmentCount; i++) {
+                scaledSegments.add(mSegments.get(i).applyEffectStrength(effectStrength));
+            }
+            if (scaledSegments.equals(mSegments)) {
+                return this;
+            }
+            Composed scaled = new Composed(scaledSegments, mRepeatIndex);
+            scaled.validate();
+            return scaled;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (!(o instanceof Composed)) {
+                return false;
+            }
+            Composed other = (Composed) o;
+            return mSegments.equals(other.mSegments) && mRepeatIndex == other.mRepeatIndex;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mSegments, mRepeatIndex);
+        }
+
+        @Override
+        public String toString() {
+            return "Composed{segments=" + mSegments
+                    + ", repeat=" + mRepeatIndex
+                    + "}";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeList(mSegments);
+            out.writeInt(mRepeatIndex);
+        }
+
+        @NonNull
+        public static final Creator<Composed> CREATOR =
+                new Creator<Composed>() {
+                    @Override
+                    public Composed createFromParcel(Parcel in) {
+                        return new Composed(in);
+                    }
+
+                    @Override
+                    public Composed[] newArray(int size) {
+                        return new Composed[size];
+                    }
+                };
+
+        /**
+         * Casts a provided {@link VibrationEffectSegment} to a {@link StepSegment} and returns it,
+         * only if it can possibly be a segment for an effect created via
+         * {@link #createWaveform(int[], int)}. Otherwise, returns {@code null}.
+         */
+        @Nullable
+        private static StepSegment castToValidStepSegmentForOffOnTimingsOrNull(
+                VibrationEffectSegment segment) {
+            if (!(segment instanceof StepSegment)) {
+                return null;
+            }
+
+            StepSegment stepSegment = (StepSegment) segment;
+            if (stepSegment.getFrequencyHz() != 0) {
+                return null;
+            }
+
+            float amplitude = stepSegment.getAmplitude();
+            if (amplitude != 0 && amplitude != DEFAULT_AMPLITUDE) {
+                return null;
+            }
+
+            return stepSegment;
+        }
+    }
+
+    /**
+     * A composition of haptic elements that are combined to be playable as a single
+     * {@link VibrationEffect}.
+     *
+     * <p>The haptic primitives are available as {@code Composition.PRIMITIVE_*} constants and
+     * can be added to a composition to create a custom vibration effect. Here is an example of an
+     * effect that grows in intensity and then dies off, with a longer rising portion for emphasis
+     * and an extra tick 100ms after:
+     *
+     * <pre>
+     * {@code VibrationEffect effect = VibrationEffect.startComposition()
+     *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f)
+     *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f)
+     *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
+     *     .compose();}</pre>
+     *
+     * <p>When choosing to play a composed effect, you should check that individual components are
+     * supported by the device by using {@link Vibrator#arePrimitivesSupported}.
+     *
+     * @see VibrationEffect#startComposition()
+     */
+    public static final class Composition {
+        /** @hide */
+        @IntDef(prefix = { "PRIMITIVE_" }, value = {
+                PRIMITIVE_CLICK,
+                PRIMITIVE_THUD,
+                PRIMITIVE_SPIN,
+                PRIMITIVE_QUICK_RISE,
+                PRIMITIVE_SLOW_RISE,
+                PRIMITIVE_QUICK_FALL,
+                PRIMITIVE_TICK,
+                PRIMITIVE_LOW_TICK,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface PrimitiveType {
+        }
+
+        /**
+         * Exception thrown when adding an element to a {@link Composition} that already ends in an
+         * indefinitely repeating effect.
+         * @hide
+         */
+        @TestApi
+        public static final class UnreachableAfterRepeatingIndefinitelyException
+                extends IllegalStateException {
+            UnreachableAfterRepeatingIndefinitelyException() {
+                super("Compositions ending in an indefinitely repeating effect can't be extended");
+            }
+        }
+
+        /**
+         * No haptic effect. Used to generate extended delays between primitives.
+         *
+         * @hide
+         */
+        public static final int PRIMITIVE_NOOP = 0;
+        /**
+         * This effect should produce a sharp, crisp click sensation.
+         */
+        public static final int PRIMITIVE_CLICK = 1;
+        /**
+         * A haptic effect that simulates downwards movement with gravity. Often
+         * followed by extra energy of hitting and reverberation to augment
+         * physicality.
+         */
+        public static final int PRIMITIVE_THUD = 2;
+        /**
+         * A haptic effect that simulates spinning momentum.
+         */
+        public static final int PRIMITIVE_SPIN = 3;
+        /**
+         * A haptic effect that simulates quick upward movement against gravity.
+         */
+        public static final int PRIMITIVE_QUICK_RISE = 4;
+        /**
+         * A haptic effect that simulates slow upward movement against gravity.
+         */
+        public static final int PRIMITIVE_SLOW_RISE = 5;
+        /**
+         * A haptic effect that simulates quick downwards movement with gravity.
+         */
+        public static final int PRIMITIVE_QUICK_FALL = 6;
+        /**
+         * This very short effect should produce a light crisp sensation intended
+         * to be used repetitively for dynamic feedback.
+         */
+        // Internally this maps to the HAL constant CompositePrimitive::LIGHT_TICK
+        public static final int PRIMITIVE_TICK = 7;
+        /**
+         * This very short low frequency effect should produce a light crisp sensation
+         * intended to be used repetitively for dynamic feedback.
+         */
+        // Internally this maps to the HAL constant CompositePrimitive::LOW_TICK
+        public static final int PRIMITIVE_LOW_TICK = 8;
+
+
+        private final ArrayList<VibrationEffectSegment> mSegments = new ArrayList<>();
+        private int mRepeatIndex = -1;
+
+        Composition() {}
+
+        /**
+         * Adds a time duration to the current composition, during which the vibrator will be
+         * turned off.
+         *
+         * @param duration The length of time the vibrator should be off. Value must be non-negative
+         *                 and will be truncated to milliseconds.
+         * @return This {@link Composition} object to enable adding multiple elements in one chain.
+         *
+         * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
+         * ending with a repeating effect.
+         * @hide
+         */
+        @TestApi
+        @NonNull
+        public Composition addOffDuration(@NonNull Duration duration) {
+            int durationMs = (int) duration.toMillis();
+            Preconditions.checkArgumentNonnegative(durationMs, "Off period must be non-negative");
+            if (durationMs > 0) {
+                // Created a segment sustaining the zero amplitude to represent the delay.
+                addSegment(new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0,
+                        (int) duration.toMillis()));
+            }
+            return this;
+        }
+
+        /**
+         * Add a haptic effect to the end of the current composition.
+         *
+         * <p>If this effect is repeating (e.g. created by {@link VibrationEffect#createWaveform}
+         * with a non-negative repeat index, or created by another composition that has effects
+         * repeating indefinitely), then no more effects or primitives will be accepted by this
+         * composition after this method. Such effects should be cancelled via
+         * {@link Vibrator#cancel()}.
+         *
+         * @param effect The effect to add to the end of this composition.
+         * @return This {@link Composition} object to enable adding multiple elements in one chain.
+         *
+         * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
+         * ending with a repeating effect.
+         * @hide
+         */
+        @TestApi
+        @NonNull
+        public Composition addEffect(@NonNull VibrationEffect effect) {
+            return addSegments(effect);
+        }
+
+        /**
+         * Add a haptic effect to the end of the current composition and play it on repeat,
+         * indefinitely.
+         *
+         * <p>The entire effect will be played on repeat, indefinitely, after all other elements
+         * already added to this composition are played. No more effects or primitives will be
+         * accepted by this composition after this method. Such effects should be cancelled via
+         * {@link Vibrator#cancel()}.
+         *
+         * @param effect The effect to add to the end of this composition, must be finite.
+         * @return This {@link Composition} object to enable adding multiple elements in one chain,
+         * although only {@link #compose()} can follow this call.
+         *
+         * @throws IllegalArgumentException if the given effect is already repeating indefinitely.
+         * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
+         * ending with a repeating effect.
+         * @hide
+         */
+        @TestApi
+        @NonNull
+        public Composition repeatEffectIndefinitely(@NonNull VibrationEffect effect) {
+            Preconditions.checkArgument(effect.getDuration() < Long.MAX_VALUE,
+                    "Can't repeat an indefinitely repeating effect. Consider addEffect instead.");
+            int previousSegmentCount = mSegments.size();
+            addSegments(effect);
+            // Set repeat after segments were added, since addSegments checks this index.
+            mRepeatIndex = previousSegmentCount;
+            return this;
+        }
+
+        /**
+         * Add a haptic primitive to the end of the current composition.
+         *
+         * <p>Similar to {@link #addPrimitive(int, float, int)}, but with no delay and a
+         * default scale applied.
+         *
+         * @param primitiveId The primitive to add
+         * @return This {@link Composition} object to enable adding multiple elements in one chain.
+         */
+        @NonNull
+        public Composition addPrimitive(@PrimitiveType int primitiveId) {
+            return addPrimitive(primitiveId, /*scale*/ 1.0f, /*delay*/ 0);
+        }
+
+        /**
+         * Add a haptic primitive to the end of the current composition.
+         *
+         * <p>Similar to {@link #addPrimitive(int, float, int)}, but with no delay.
+         *
+         * @param primitiveId The primitive to add
+         * @param scale The scale to apply to the intensity of the primitive.
+         * @return This {@link Composition} object to enable adding multiple elements in one chain.
+         */
+        @NonNull
+        public Composition addPrimitive(@PrimitiveType int primitiveId,
+                @FloatRange(from = 0f, to = 1f) float scale) {
+            return addPrimitive(primitiveId, scale, /*delay*/ 0);
+        }
+
+        /**
+         * Add a haptic primitive to the end of the current composition.
+         *
+         * @param primitiveId The primitive to add
+         * @param scale The scale to apply to the intensity of the primitive.
+         * @param delay The amount of time in milliseconds to wait before playing this primitive,
+         *              starting at the time the previous element in this composition is finished.
+         * @return This {@link Composition} object to enable adding multiple elements in one chain.
+         */
+        @NonNull
+        public Composition addPrimitive(@PrimitiveType int primitiveId,
+                @FloatRange(from = 0f, to = 1f) float scale, @IntRange(from = 0) int delay) {
+            PrimitiveSegment primitive = new PrimitiveSegment(primitiveId, scale,
+                    delay);
+            primitive.validate();
+            return addSegment(primitive);
+        }
+
+        private Composition addSegment(VibrationEffectSegment segment) {
+            if (mRepeatIndex >= 0) {
+                throw new UnreachableAfterRepeatingIndefinitelyException();
+            }
+            mSegments.add(segment);
+            return this;
+        }
+
+        private Composition addSegments(VibrationEffect effect) {
+            if (mRepeatIndex >= 0) {
+                throw new UnreachableAfterRepeatingIndefinitelyException();
+            }
+            Composed composed = (Composed) effect;
+            if (composed.getRepeatIndex() >= 0) {
+                // Start repeating from the index relative to the composed waveform.
+                mRepeatIndex = mSegments.size() + composed.getRepeatIndex();
+            }
+            mSegments.addAll(composed.getSegments());
+            return this;
+        }
+
+        /**
+         * Compose all of the added primitives together into a single {@link VibrationEffect}.
+         *
+         * <p>The {@link Composition} object is still valid after this call, so you can continue
+         * adding more primitives to it and generating more {@link VibrationEffect}s by calling this
+         * method again.
+         *
+         * @return The {@link VibrationEffect} resulting from the composition of the primitives.
+         */
+        @NonNull
+        public VibrationEffect compose() {
+            if (mSegments.isEmpty()) {
+                throw new IllegalStateException(
+                        "Composition must have at least one element to compose.");
+            }
+            VibrationEffect effect = new Composed(mSegments, mRepeatIndex);
+            effect.validate();
+            return effect;
+        }
+
+        /**
+         * Convert the primitive ID to a human readable string for debugging.
+         * @param id The ID to convert
+         * @return The ID in a human readable format.
+         * @hide
+         */
+        public static String primitiveToString(@PrimitiveType int id) {
+            switch (id) {
+                case PRIMITIVE_NOOP:
+                    return "PRIMITIVE_NOOP";
+                case PRIMITIVE_CLICK:
+                    return "PRIMITIVE_CLICK";
+                case PRIMITIVE_THUD:
+                    return "PRIMITIVE_THUD";
+                case PRIMITIVE_SPIN:
+                    return "PRIMITIVE_SPIN";
+                case PRIMITIVE_QUICK_RISE:
+                    return "PRIMITIVE_QUICK_RISE";
+                case PRIMITIVE_SLOW_RISE:
+                    return "PRIMITIVE_SLOW_RISE";
+                case PRIMITIVE_QUICK_FALL:
+                    return "PRIMITIVE_QUICK_FALL";
+                case PRIMITIVE_TICK:
+                    return "PRIMITIVE_TICK";
+                case PRIMITIVE_LOW_TICK:
+                    return "PRIMITIVE_LOW_TICK";
+                default:
+                    return Integer.toString(id);
+            }
+        }
+    }
+
+    /**
+     * A builder for waveform haptic effects.
+     *
+     * <p>Waveform vibrations constitute of one or more timed transitions to new sets of vibration
+     * parameters. These parameters can be the vibration amplitude, frequency, or both.
+     *
+     * <p>The following example ramps a vibrator turned off to full amplitude at 120Hz, over 100ms
+     * starting at 60Hz, then holds that state for 200ms and ramps back down again over 100ms:
+     *
+     * <pre>
+     * {@code import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
+     * import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
+     *
+     * VibrationEffect effect = VibrationEffect.startWaveform(targetFrequency(60))
+     *     .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
+     *     .addSustain(Duration.ofMillis(200))
+     *     .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
+     *     .build();}</pre>
+     *
+     * <p>The initial state of the waveform can be set via
+     * {@link VibrationEffect#startWaveform(VibrationParameter)} or
+     * {@link VibrationEffect#startWaveform(VibrationParameter, VibrationParameter)}. If the initial
+     * parameters are not set then the {@link WaveformBuilder} will start with the vibrator off,
+     * represented by zero amplitude, at the vibrator's resonant frequency.
+     *
+     * <p>Repeating waveforms can be created by building the repeating block separately and adding
+     * it to the end of a composition with
+     * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}:
+     *
+     * <p>Note that physical vibration actuators have different reaction times for changing
+     * amplitude and frequency. Durations specified here represent a timeline for the target
+     * parameters, and quality of effects may be improved if the durations allow time for a
+     * transition to be smoothly applied.
+     *
+     * <p>The following example illustrates both an initial state and a repeating section, using
+     * a {@link VibrationEffect.Composition}. The resulting effect will have a tick followed by a
+     * repeated beating effect with a rise that stretches out and a sharp finish.
+     *
+     * <pre>
+     * {@code VibrationEffect patternToRepeat = VibrationEffect.startWaveform(targetAmplitude(0.2f))
+     *     .addSustain(Duration.ofMillis(10))
+     *     .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f))
+     *     .addSustain(Duration.ofMillis(30))
+     *     .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f))
+     *     .addSustain(Duration.ofMillis(50))
+     *     .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f))
+     *     .build();
+     *
+     * VibrationEffect effect = VibrationEffect.startComposition()
+     *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+     *     .addOffDuration(Duration.ofMillis(20))
+     *     .repeatEffectIndefinitely(patternToRepeat)
+     *     .compose();}</pre>
+     *
+     * <p>The amplitude step waveforms that can be created via
+     * {@link VibrationEffect#createWaveform(long[], int[], int)} can also be created with
+     * {@link WaveformBuilder} by adding zero duration transitions:
+     *
+     * <pre>
+     * {@code // These two effects are the same
+     * VibrationEffect waveform = VibrationEffect.createWaveform(
+     *     new long[] { 10, 20, 30 },  // timings in milliseconds
+     *     new int[] { 51, 102, 204 }, // amplitudes in [0,255]
+     *     -1);                        // repeat index
+     *
+     * VibrationEffect sameWaveform = VibrationEffect.startWaveform(targetAmplitude(0.2f))
+     *     .addSustain(Duration.ofMillis(10))
+     *     .addTransition(Duration.ZERO, targetAmplitude(0.4f))
+     *     .addSustain(Duration.ofMillis(20))
+     *     .addTransition(Duration.ZERO, targetAmplitude(0.8f))
+     *     .addSustain(Duration.ofMillis(30))
+     *     .build();}</pre>
+     *
+     * @see VibrationEffect#startWaveform
+     * @hide
+     */
+    @TestApi
+    public static final class WaveformBuilder {
+        // Epsilon used for float comparison of amplitude and frequency values on transitions.
+        private static final float EPSILON = 1e-5f;
+
+        private ArrayList<VibrationEffectSegment> mSegments = new ArrayList<>();
+        private float mLastAmplitude = 0f;
+        private float mLastFrequencyHz = 0f;
+
+        WaveformBuilder() {}
+
+        /**
+         * Add a transition to new vibration parameter value to the end of this waveform.
+         *
+         * <p>The duration represents how long the vibrator should take to smoothly transition to
+         * the new vibration parameter. If the duration is zero then the vibrator will jump to the
+         * new value as fast as possible.
+         *
+         * <p>Vibration parameter values will be truncated to conform to the device capabilities
+         * according to the {@link android.os.vibrator.VibratorFrequencyProfile}.
+         *
+         * @param duration        The length of time this transition should take. Value must be
+         *                        non-negative and will be truncated to milliseconds.
+         * @param targetParameter The new target {@link VibrationParameter} value to be reached
+         *                        after the given duration.
+         * @return This {@link WaveformBuilder} object to enable adding multiple transitions in
+         * chain.
+         * @hide
+         */
+        @TestApi
+        @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
+        @NonNull
+        public WaveformBuilder addTransition(@NonNull Duration duration,
+                @NonNull VibrationParameter targetParameter) {
+            Preconditions.checkNotNull(duration, "Duration is null");
+            checkVibrationParameter(targetParameter, "targetParameter");
+            float amplitude = extractTargetAmplitude(targetParameter, /* target2= */ null);
+            float frequencyHz = extractTargetFrequency(targetParameter, /* target2= */ null);
+            addTransitionSegment(duration, amplitude, frequencyHz);
+            return this;
+        }
+
+        /**
+         * Add a transition to new vibration parameters to the end of this waveform.
+         *
+         * <p>The duration represents how long the vibrator should take to smoothly transition to
+         * the new vibration parameters. If the duration is zero then the vibrator will jump to the
+         * new values as fast as possible.
+         *
+         * <p>Vibration parameters values will be truncated to conform to the device capabilities
+         * according to the {@link android.os.vibrator.VibratorFrequencyProfile}.
+         *
+         * @param duration         The length of time this transition should take. Value must be
+         *                         non-negative and will be truncated to milliseconds.
+         * @param targetParameter1 The first target {@link VibrationParameter} value to be reached
+         *                         after the given duration.
+         * @param targetParameter2 The second target {@link VibrationParameter} value to be reached
+         *                         after the given duration, must be a different type of parameter
+         *                         than the one specified by the first argument.
+         * @return This {@link WaveformBuilder} object to enable adding multiple transitions in
+         * chain.
+         * @hide
+         */
+        @TestApi
+        @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
+        @NonNull
+        public WaveformBuilder addTransition(@NonNull Duration duration,
+                @NonNull VibrationParameter targetParameter1,
+                @NonNull VibrationParameter targetParameter2) {
+            Preconditions.checkNotNull(duration, "Duration is null");
+            checkVibrationParameter(targetParameter1, "targetParameter1");
+            checkVibrationParameter(targetParameter2, "targetParameter2");
+            Preconditions.checkArgument(
+                    !Objects.equals(targetParameter1.getClass(), targetParameter2.getClass()),
+                    "Parameter arguments must specify different parameter types");
+            float amplitude = extractTargetAmplitude(targetParameter1, targetParameter2);
+            float frequencyHz = extractTargetFrequency(targetParameter1, targetParameter2);
+            addTransitionSegment(duration, amplitude, frequencyHz);
+            return this;
+        }
+
+        /**
+         * Add a duration to sustain the last vibration parameters of this waveform.
+         *
+         * <p>The duration represents how long the vibrator should sustain the last set of
+         * parameters provided to this builder.
+         *
+         * @param duration   The length of time the last values should be sustained by the vibrator.
+         *                   Value must be >= 1ms.
+         * @return This {@link WaveformBuilder} object to enable adding multiple transitions in
+         * chain.
+         * @hide
+         */
+        @TestApi
+        @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
+        @NonNull
+        public WaveformBuilder addSustain(@NonNull Duration duration) {
+            int durationMs = (int) duration.toMillis();
+            Preconditions.checkArgument(durationMs >= 1, "Sustain duration must be >= 1ms");
+            mSegments.add(new StepSegment(mLastAmplitude, mLastFrequencyHz, durationMs));
+            return this;
+        }
+
+        /**
+         * Build the waveform as a single {@link VibrationEffect}.
+         *
+         * <p>The {@link WaveformBuilder} object is still valid after this call, so you can
+         * continue adding more primitives to it and generating more {@link VibrationEffect}s by
+         * calling this method again.
+         *
+         * @return The {@link VibrationEffect} resulting from the list of transitions.
+         * @hide
+         */
+        @TestApi
+        @NonNull
+        public VibrationEffect build() {
+            if (mSegments.isEmpty()) {
+                throw new IllegalStateException(
+                        "WaveformBuilder must have at least one transition to build.");
+            }
+            VibrationEffect effect = new Composed(mSegments, /* repeatIndex= */ -1);
+            effect.validate();
+            return effect;
+        }
+
+        private void checkVibrationParameter(@NonNull VibrationParameter vibrationParameter,
+                String paramName) {
+            Preconditions.checkNotNull(vibrationParameter, "%s is null", paramName);
+            Preconditions.checkArgument(
+                    (vibrationParameter instanceof AmplitudeVibrationParameter)
+                            || (vibrationParameter instanceof FrequencyVibrationParameter),
+                    "%s is a unknown parameter", paramName);
+        }
+
+        private float extractTargetAmplitude(@Nullable VibrationParameter target1,
+                @Nullable VibrationParameter target2) {
+            if (target2 instanceof AmplitudeVibrationParameter) {
+                return ((AmplitudeVibrationParameter) target2).amplitude;
+            }
+            if (target1 instanceof AmplitudeVibrationParameter) {
+                return ((AmplitudeVibrationParameter) target1).amplitude;
+            }
+            return mLastAmplitude;
+        }
+
+        private float extractTargetFrequency(@Nullable VibrationParameter target1,
+                @Nullable VibrationParameter target2) {
+            if (target2 instanceof FrequencyVibrationParameter) {
+                return ((FrequencyVibrationParameter) target2).frequencyHz;
+            }
+            if (target1 instanceof FrequencyVibrationParameter) {
+                return ((FrequencyVibrationParameter) target1).frequencyHz;
+            }
+            return mLastFrequencyHz;
+        }
+
+        private void addTransitionSegment(Duration duration, float targetAmplitude,
+                float targetFrequency) {
+            Preconditions.checkNotNull(duration, "Duration is null");
+            Preconditions.checkArgument(!duration.isNegative(),
+                    "Transition duration must be non-negative");
+            int durationMs = (int) duration.toMillis();
+
+            // Ignore transitions with zero duration, but keep values for next additions.
+            if (durationMs > 0) {
+                if ((Math.abs(mLastAmplitude - targetAmplitude) < EPSILON)
+                        && (Math.abs(mLastFrequencyHz - targetFrequency) < EPSILON)) {
+                    // No value is changing, this can be best represented by a step segment.
+                    mSegments.add(new StepSegment(targetAmplitude, targetFrequency, durationMs));
+                } else {
+                    mSegments.add(new RampSegment(mLastAmplitude, targetAmplitude,
+                            mLastFrequencyHz, targetFrequency, durationMs));
+                }
+            }
+
+            mLastAmplitude = targetAmplitude;
+            mLastFrequencyHz = targetFrequency;
+        }
+    }
+
+    /**
+     * A representation of a single vibration parameter.
+     *
+     * <p>This is to describe a waveform haptic effect, which consists of one or more timed
+     * transitions to a new set of {@link VibrationParameter}s.
+     *
+     * <p>Examples of concrete parameters are the vibration amplitude or frequency.
+     *
+     * @see VibrationEffect.WaveformBuilder
+     * @hide
+     */
+    @TestApi
+    @SuppressWarnings("UserHandleName") // This is not a regular set of parameters, no *Params.
+    public static class VibrationParameter {
+        VibrationParameter() {
+        }
+
+        /**
+         * The target vibration amplitude.
+         *
+         * @param amplitude The amplitude value, between 0 and 1, inclusive, where 0 represents the
+         *                  vibrator turned off and 1 represents the maximum amplitude the vibrator
+         *                  can reach across all supported frequencies.
+         * @return The {@link VibrationParameter} instance that represents given amplitude.
+         * @hide
+         */
+        @TestApi
+        @NonNull
+        public static VibrationParameter targetAmplitude(
+                @FloatRange(from = 0, to = 1) float amplitude) {
+            return new AmplitudeVibrationParameter(amplitude);
+        }
+
+        /**
+         * The target vibration frequency.
+         *
+         * @param frequencyHz The frequency value, in hertz.
+         * @return The {@link VibrationParameter} instance that represents given frequency.
+         * @hide
+         */
+        @TestApi
+        @NonNull
+        public static VibrationParameter targetFrequency(@FloatRange(from = 1) float frequencyHz) {
+            return new FrequencyVibrationParameter(frequencyHz);
+        }
+    }
+
+    /** The vibration amplitude, represented by a value in [0,1]. */
+    private static final class AmplitudeVibrationParameter extends VibrationParameter {
+        public final float amplitude;
+
+        AmplitudeVibrationParameter(float amplitude) {
+            Preconditions.checkArgument((amplitude >= 0) && (amplitude <= 1),
+                    "Amplitude must be within [0,1]");
+            this.amplitude = amplitude;
+        }
+    }
+
+    /** The vibration frequency, in hertz, or zero to represent undefined frequency. */
+    private static final class FrequencyVibrationParameter extends VibrationParameter {
+        public final float frequencyHz;
+
+        FrequencyVibrationParameter(float frequencyHz) {
+            Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1");
+            Preconditions.checkArgument(Float.isFinite(frequencyHz), "Frequency must be finite");
+            this.frequencyHz = frequencyHz;
+        }
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<VibrationEffect> CREATOR =
+            new Parcelable.Creator<VibrationEffect>() {
+                @Override
+                public VibrationEffect createFromParcel(Parcel in) {
+                    return new Composed(in);
+                }
+                @Override
+                public VibrationEffect[] newArray(int size) {
+                    return new VibrationEffect[size];
+                }
+            };
+}
diff --git a/android-34/android/os/Vibrator.java b/android-34/android/os/Vibrator.java
new file mode 100644
index 0000000..4e852e3
--- /dev/null
+++ b/android-34/android/os/Vibrator.java
@@ -0,0 +1,751 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.vibrator.IVibrator;
+import android.media.AudioAttributes;
+import android.os.vibrator.VibrationConfig;
+import android.os.vibrator.VibratorFrequencyProfile;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Class that operates the vibrator on the device.
+ * <p>
+ * If your process exits, any vibration you started will stop.
+ * </p>
+ */
+@SystemService(Context.VIBRATOR_SERVICE)
+public abstract class Vibrator {
+    private static final String TAG = "Vibrator";
+
+    /**
+     * Vibration intensity: no vibrations.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int VIBRATION_INTENSITY_OFF = 0;
+
+    /**
+     * Vibration intensity: low.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int VIBRATION_INTENSITY_LOW = 1;
+
+    /**
+     * Vibration intensity: medium.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int VIBRATION_INTENSITY_MEDIUM = 2;
+
+    /**
+     * Vibration intensity: high.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int VIBRATION_INTENSITY_HIGH = 3;
+
+    /**
+     * Vibration effect support: unknown
+     *
+     * <p>The hardware doesn't report its supported effects, so we can't determine whether the
+     * effect is supported or not.
+     */
+    public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0;
+
+    /**
+     * Vibration effect support: supported
+     *
+     * <p>This effect is supported by the underlying hardware.
+     */
+    public static final int VIBRATION_EFFECT_SUPPORT_YES = 1;
+
+    /**
+     * Vibration effect support: unsupported
+     *
+     * <p>This effect is <b>not</b> natively supported by the underlying hardware, although
+     * the system may still play a fallback vibration.
+     */
+    public static final int VIBRATION_EFFECT_SUPPORT_NO = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"VIBRATION_EFFECT_SUPPORT_"}, value = {
+            VIBRATION_EFFECT_SUPPORT_UNKNOWN,
+            VIBRATION_EFFECT_SUPPORT_YES,
+            VIBRATION_EFFECT_SUPPORT_NO,
+    })
+    public @interface VibrationEffectSupport {}
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"VIBRATION_INTENSITY_"}, value = {
+            VIBRATION_INTENSITY_OFF,
+            VIBRATION_INTENSITY_LOW,
+            VIBRATION_INTENSITY_MEDIUM,
+            VIBRATION_INTENSITY_HIGH
+    })
+    public @interface VibrationIntensity {
+    }
+
+    private final String mPackageName;
+    @Nullable
+    private final Resources mResources;
+
+    // This is lazily loaded only for the few clients that need this (e. Settings app).
+    @Nullable
+    private volatile VibrationConfig mVibrationConfig;
+
+    /**
+     * @hide to prevent subclassing from outside of the framework
+     */
+    @UnsupportedAppUsage
+    public Vibrator() {
+        mPackageName = ActivityThread.currentPackageName();
+        mResources = null;
+    }
+
+    /**
+     * @hide to prevent subclassing from outside of the framework
+     */
+    protected Vibrator(Context context) {
+        mPackageName = context.getOpPackageName();
+        mResources = context.getResources();
+    }
+
+    /**
+     * Get the info describing this vibrator.
+     *
+     * @hide
+     */
+    protected VibratorInfo getInfo() {
+        return VibratorInfo.EMPTY_VIBRATOR_INFO;
+    }
+
+    /** Get the static vibrator configuration from config.xml. */
+    private VibrationConfig getConfig() {
+        if (mVibrationConfig == null) {
+            Resources resources = mResources;
+            if (resources == null) {
+                final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
+                resources = ctx != null ? ctx.getResources() : null;
+            }
+            // This might be constructed more than once, but it only loads static config data from a
+            // xml file, so it would be ok.
+            mVibrationConfig = new VibrationConfig(resources);
+        }
+        return mVibrationConfig;
+    }
+
+    /**
+     * Get the default vibration intensity for given usage.
+     *
+     * @hide
+     */
+    @TestApi
+    @VibrationIntensity
+    public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
+        return getConfig().getDefaultVibrationIntensity(usage);
+    }
+
+    /**
+     * Return the ID of this vibrator.
+     *
+     * @return A non-negative integer representing the id of the vibrator controlled by this
+     * service, or -1 this service is not attached to any physical vibrator.
+     */
+    public int getId() {
+        return getInfo().getId();
+    }
+
+    /**
+     * Check whether the hardware has a vibrator.
+     *
+     * @return True if the hardware has a vibrator, else false.
+     */
+    public abstract boolean hasVibrator();
+
+    /**
+     * Check whether the vibrator has amplitude control.
+     *
+     * @return True if the hardware can control the amplitude of the vibrations, otherwise false.
+     */
+    public abstract boolean hasAmplitudeControl();
+
+    /**
+     * Check whether the vibrator has independent frequency control.
+     *
+     * @return True if the hardware can control the frequency of the vibrations independently of
+     * the vibration amplitude, false otherwise.
+     * @hide
+     */
+    @TestApi
+    public boolean hasFrequencyControl() {
+        // We currently can only control frequency of the vibration using the compose PWLE method.
+        return getInfo().hasCapability(
+                IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+    }
+
+    /**
+     * Checks whether or not the vibrator supports all components of a given {@link VibrationEffect}
+     * (i.e. the vibrator can play the given effect as intended).
+     *
+     * <p>If this method returns {@code true}, then the VibrationEffect should play as expected.
+     * If {@code false}, playing the VibrationEffect might still make a vibration, but the vibration
+     * may be significantly degraded from the intention.
+     *
+     * <p>This method aggregates the results of feature check methods such as
+     * {@link #hasAmplitudeControl}, {@link #areAllPrimitivesSupported(int...)}, etc, depending
+     * on the features that are actually used by the VibrationEffect.
+     *
+     * @param effect the {@link VibrationEffect} to check if it is supported
+     * @return {@code true} if the vibrator can play the given {@code effect} as intended,
+     *         {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) {
+        return effect.areVibrationFeaturesSupported(this);
+    }
+
+    /**
+     * Check whether the vibrator can be controlled by an external service with the
+     * {@link IExternalVibratorService}.
+     *
+     * @return True if the hardware can be controlled by an external service, otherwise false.
+     * @hide
+     */
+    public boolean hasExternalControl() {
+        return getInfo().hasCapability(IVibrator.CAP_EXTERNAL_CONTROL);
+    }
+
+    /**
+     * Gets the resonant frequency of the vibrator, if applicable.
+     *
+     * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
+     * applicable, or if this vibrator is a composite of multiple physical devices with different
+     * frequencies.
+     */
+    public float getResonantFrequency() {
+        return getInfo().getResonantFrequencyHz();
+    }
+
+    /**
+     * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+     *
+     * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
+     * applicable, or if this vibrator is a composite of multiple physical devices with different
+     * Q factors.
+     */
+    public float getQFactor() {
+        return getInfo().getQFactor();
+    }
+
+    /**
+     * Gets the profile that describes the vibrator output across the supported frequency range.
+     *
+     * <p>The profile describes the relative output acceleration that the device can reach when it
+     * vibrates at different frequencies.
+     *
+     * @return The frequency profile for this vibrator, or null if the vibrator does not have
+     * frequency control. If this vibrator is a composite of multiple physical devices then this
+     * will return a profile supported in all devices, or null if the intersection is empty or not
+     * available.
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public VibratorFrequencyProfile getFrequencyProfile() {
+        VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile();
+        if (frequencyProfile.isEmpty()) {
+            return null;
+        }
+        return new VibratorFrequencyProfile(frequencyProfile);
+    }
+
+    /**
+     * Return the maximum amplitude the vibrator can play using the audio haptic channels.
+     *
+     * <p>This is a positive value, or {@link Float#NaN NaN} if it's unknown. If this returns a
+     * positive value <code>maxAmplitude</code>, then the signals from the haptic channels of audio
+     * tracks should be in the range <code>[-maxAmplitude, maxAmplitude]</code>.
+     *
+     * @return a positive value representing the maximum absolute value the device can play signals
+     * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown.
+     * @hide
+     */
+    public float getHapticChannelMaximumAmplitude() {
+        return getConfig().getHapticChannelMaximumAmplitude();
+    }
+
+    /**
+     * Configure an always-on haptics effect.
+     *
+     * @param alwaysOnId The board-specific always-on ID to configure.
+     * @param effect     Vibration effect to assign to always-on id. Passing null will disable it.
+     * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
+     *                   specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
+     *                   {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with
+     *                   incoming calls. May only be null when effect is null.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
+    public boolean setAlwaysOnEffect(int alwaysOnId, @Nullable VibrationEffect effect,
+            @Nullable VibrationAttributes attributes) {
+        return setAlwaysOnEffect(Process.myUid(), mPackageName, alwaysOnId, effect, attributes);
+    }
+
+    /**
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
+    public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+            @Nullable VibrationEffect effect, @Nullable VibrationAttributes attributes) {
+        Log.w(TAG, "Always-on effects aren't supported");
+        return false;
+    }
+
+    /**
+     * Vibrate constantly for the specified period of time.
+     *
+     * <p>The app should be in the foreground for the vibration to happen.</p>
+     *
+     * @param milliseconds The number of milliseconds to vibrate.
+     * @deprecated Use {@link #vibrate(VibrationEffect)} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(long milliseconds) {
+        vibrate(milliseconds, null);
+    }
+
+    /**
+     * Vibrate constantly for the specified period of time.
+     *
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
+     * @param milliseconds The number of milliseconds to vibrate.
+     * @param attributes   {@link AudioAttributes} corresponding to the vibration. For example,
+     *                     specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+     *                     {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+     *                     vibrations associated with incoming calls.
+     * @deprecated Use {@link #vibrate(VibrationEffect, VibrationAttributes)} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(long milliseconds, AudioAttributes attributes) {
+        try {
+            // This ignores all exceptions to stay compatible with pre-O implementations.
+            VibrationEffect effect =
+                    VibrationEffect.createOneShot(milliseconds, VibrationEffect.DEFAULT_AMPLITUDE);
+            vibrate(effect, attributes);
+        } catch (IllegalArgumentException iae) {
+            Log.e(TAG, "Failed to create VibrationEffect", iae);
+        }
+    }
+
+    /**
+     * Vibrate with a given pattern.
+     *
+     * <p>
+     * Pass in an array of ints that are the durations for which to turn on or off
+     * the vibrator in milliseconds.  The first value indicates the number of milliseconds
+     * to wait before turning the vibrator on.  The next value indicates the number of milliseconds
+     * for which to keep the vibrator on before turning it off.  Subsequent values alternate
+     * between durations in milliseconds to turn the vibrator off or to turn the vibrator on.
+     * </p><p>
+     * To cause the pattern to repeat, pass the index into the pattern array at which
+     * to start the repeat, or -1 to disable repeating.
+     * </p>
+     *
+     * <p>The app should be in the foreground for the vibration to happen.</p>
+     *
+     * @param pattern an array of longs of times for which to turn the vibrator on or off.
+     * @param repeat  the index into pattern at which to repeat, or -1 if
+     *                you don't want to repeat.
+     * @deprecated Use {@link #vibrate(VibrationEffect)} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(long[] pattern, int repeat) {
+        vibrate(pattern, repeat, null);
+    }
+
+    /**
+     * Vibrate with a given pattern.
+     *
+     * <p>
+     * Pass in an array of ints that are the durations for which to turn on or off
+     * the vibrator in milliseconds.  The first value indicates the number of milliseconds
+     * to wait before turning the vibrator on.  The next value indicates the number of milliseconds
+     * for which to keep the vibrator on before turning it off.  Subsequent values alternate
+     * between durations in milliseconds to turn the vibrator off or to turn the vibrator on.
+     * </p><p>
+     * To cause the pattern to repeat, pass the index into the pattern array at which
+     * to start the repeat, or -1 to disable repeating.
+     * </p>
+     *
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
+     * @param pattern    an array of longs of times for which to turn the vibrator on or off.
+     * @param repeat     the index into pattern at which to repeat, or -1 if
+     *                   you don't want to repeat.
+     * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
+     *                   specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+     *                   {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+     *                   vibrations associated with incoming calls.
+     * @deprecated Use {@link #vibrate(VibrationEffect, VibrationAttributes)} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(long[] pattern, int repeat, AudioAttributes attributes) {
+        // This call needs to continue throwing ArrayIndexOutOfBoundsException but ignore all other
+        // exceptions for compatibility purposes
+        if (repeat < -1 || repeat >= pattern.length) {
+            Log.e(TAG, "vibrate called with repeat index out of bounds" +
+                    " (pattern.length=" + pattern.length + ", index=" + repeat + ")");
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+        try {
+            vibrate(VibrationEffect.createWaveform(pattern, repeat), attributes);
+        } catch (IllegalArgumentException iae) {
+            Log.e(TAG, "Failed to create VibrationEffect", iae);
+        }
+    }
+
+    /**
+     * Vibrate with a given effect.
+     *
+     * <p>The app should be in the foreground for the vibration to happen.</p>
+     *
+     * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(VibrationEffect vibe) {
+        vibrate(vibe, new VibrationAttributes.Builder().build());
+    }
+
+    /**
+     * Vibrate with a given effect.
+     *
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
+     * @param vibe       {@link VibrationEffect} describing the vibration to be performed.
+     * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
+     *                   specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+     *                   {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+     *                   vibrations associated with incoming calls.
+     * @deprecated Use {@link #vibrate(VibrationEffect, VibrationAttributes)} instead.
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(VibrationEffect vibe, AudioAttributes attributes) {
+        vibrate(vibe,
+                attributes == null
+                        ? new VibrationAttributes.Builder().build()
+                        : new VibrationAttributes.Builder(attributes).build());
+    }
+
+    /**
+     * Vibrate with a given effect.
+     *
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
+     * @param vibe       {@link VibrationEffect} describing the vibration to be performed.
+     * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
+     *                   specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
+     *                   {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with
+     *                   incoming calls.
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(@NonNull VibrationEffect vibe, @NonNull VibrationAttributes attributes) {
+        vibrate(Process.myUid(), mPackageName, vibe, null, attributes);
+    }
+
+    /**
+     * Like {@link #vibrate(VibrationEffect, VibrationAttributes)}, but allows the
+     * caller to specify the vibration is owned by someone else and set a reason for vibration.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public abstract void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe,
+            String reason, @NonNull VibrationAttributes attributes);
+
+    /**
+     * Query whether the vibrator natively supports the given effects.
+     *
+     * <p>If an effect is not supported, the system may still automatically fall back to playing
+     * a simpler vibration instead, which is not optimised for the specific device. This includes
+     * the unknown case, which can't be determined in advance, that will dynamically attempt to
+     * fall back if the optimised effect fails to play.
+     *
+     * <p>The returned array will be the same length as the query array and the value at a given
+     * index will contain {@link #VIBRATION_EFFECT_SUPPORT_YES} if the effect at that same index
+     * in the querying array is supported, {@link #VIBRATION_EFFECT_SUPPORT_NO} if it isn't
+     * supported, or {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether
+     * it's supported or not, as some hardware doesn't report its effect capabilities.
+     *
+     * <p>Use {@link #areAllEffectsSupported(int...)} to get a single combined result,
+     * or for convenience when querying exactly one effect.
+     *
+     * @param effectIds Which effects to query for.
+     * @return An array containing the systems current knowledge about whether the given effects
+     * are natively supported by the device, or not.
+     */
+    @NonNull
+    @VibrationEffectSupport
+    public int[] areEffectsSupported(
+            @NonNull @VibrationEffect.EffectType int... effectIds) {
+        VibratorInfo info = getInfo();
+        int[] supported = new int[effectIds.length];
+        for (int i = 0; i < effectIds.length; i++) {
+            supported[i] = info.isEffectSupported(effectIds[i]);
+        }
+        return supported;
+    }
+
+    /**
+     * Query whether the vibrator supports all the given effects. If no argument is provided this
+     * method will always return {@link #VIBRATION_EFFECT_SUPPORT_YES}.
+     *
+     * <p>If an effect is not supported, the system may still automatically fall back to a simpler
+     * vibration instead, which is not optimised for the specific device, however vibration isn't
+     * guaranteed in this case.
+     *
+     * <p>If the result is {@link #VIBRATION_EFFECT_SUPPORT_YES}, all effects in the query are
+     * supported by the hardware.
+     *
+     * <p>If the result is {@link #VIBRATION_EFFECT_SUPPORT_NO}, at least one of the effects in the
+     * query is not supported, and using them may fall back to an un-optimized vibration or no
+     * vibration.
+     *
+     * <p>If the result is {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN}, the system doesn't know
+     * whether all the effects are supported. It may support any or all of the queried effects,
+     * but there's no way to programmatically know whether a {@link #vibrate} call will successfully
+     * cause a vibration. It's guaranteed, however, that none of the queried effects are
+     * definitively unsupported by the hardware.
+     *
+     * <p>Use {@link #areEffectsSupported(int...)} to get individual results for each effect.
+     *
+     * @param effectIds Which effects to query for.
+     * @return Whether all specified effects are natively supported by the device. Empty query
+     * defaults to {@link #VIBRATION_EFFECT_SUPPORT_YES}.
+     */
+    @VibrationEffectSupport
+    public final int areAllEffectsSupported(
+            @NonNull @VibrationEffect.EffectType int... effectIds) {
+        VibratorInfo info = getInfo();
+        int allSupported = VIBRATION_EFFECT_SUPPORT_YES;
+        for (int effectId : effectIds) {
+            switch (info.isEffectSupported(effectId)) {
+                case VIBRATION_EFFECT_SUPPORT_NO:
+                    return VIBRATION_EFFECT_SUPPORT_NO;
+                case VIBRATION_EFFECT_SUPPORT_YES:
+                    continue;
+                default: // VIBRATION_EFFECT_SUPPORT_UNKNOWN
+                    allSupported = VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+                    break;
+            }
+        }
+        return allSupported;
+    }
+
+    /**
+     * Query whether the vibrator supports the given primitives.
+     *
+     * The returned array will be the same length as the query array and the value at a given index
+     * will contain whether the effect at that same index in the querying array is supported or
+     * not.
+     *
+     * <p>If a primitive is not supported by the device, then <em>no vibration</em> will occur if
+     * it is played.
+     *
+     * <p>Use {@link #areAllPrimitivesSupported(int...)} to get a single combined result,
+     * or for convenience when querying exactly one primitive.
+     *
+     * @param primitiveIds Which primitives to query for.
+     * @return Whether the primitives are supported.
+     */
+    @NonNull
+    public boolean[] arePrimitivesSupported(
+            @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
+        VibratorInfo info = getInfo();
+        boolean[] supported = new boolean[primitiveIds.length];
+        for (int i = 0; i < primitiveIds.length; i++) {
+            supported[i] = info.isPrimitiveSupported(primitiveIds[i]);
+        }
+        return supported;
+    }
+
+    /**
+     * Query whether the vibrator supports all of the given primitives.  If no argument is provided
+     * this method will always return {@code true}.
+     *
+     * <p>If a primitive is not supported by the device, then <em>no vibration</em> will occur if
+     * it is played.
+     *
+     * <p>Use {@link #arePrimitivesSupported(int...)} to get individual results for each primitive.
+     *
+     * @param primitiveIds Which primitives to query for.
+     * @return Whether all specified primitives are supported. Empty query defaults to {@code true}.
+     */
+    public final boolean areAllPrimitivesSupported(
+            @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
+        VibratorInfo info = getInfo();
+        for (int primitiveId : primitiveIds) {
+            if (!info.isPrimitiveSupported(primitiveId)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Query the estimated durations of the given primitives.
+     *
+     * <p>The returned array will be the same length as the query array and the value at a given
+     * index will contain the duration in milliseconds of the effect at the same index in the
+     * querying array.
+     *
+     * <p>The duration will be positive for primitives that are supported and zero for the
+     * unsupported ones, in correspondence with {@link #arePrimitivesSupported(int...)}.
+     *
+     * @param primitiveIds Which primitives to query for.
+     * @return The duration of each primitive, with zeroes for primitives that are not supported.
+     */
+    @NonNull
+    public int[] getPrimitiveDurations(
+            @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
+        VibratorInfo info = getInfo();
+        int[] durations = new int[primitiveIds.length];
+        for (int i = 0; i < primitiveIds.length; i++) {
+            durations[i] = info.getPrimitiveDuration(primitiveIds[i]);
+        }
+        return durations;
+    }
+
+    /**
+     * Turn the vibrator off.
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public abstract void cancel();
+
+    /**
+     * Cancel specific types of ongoing vibrations.
+     *
+     * @param usageFilter The type of vibration to be cancelled, represented as a bitwise
+     *                    combination of {@link VibrationAttributes.Usage} values.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public abstract void cancel(int usageFilter);
+
+    /**
+     * Check whether the vibrator is vibrating.
+     *
+     * @return True if the hardware is vibrating, otherwise false.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public boolean isVibrating() {
+        return false;
+    }
+
+    /**
+    * Listener for when the vibrator state has changed.
+    *
+    * @see #addVibratorStateListener
+    * @see #removeVibratorStateListener
+    * @hide
+    */
+    @SystemApi
+    public interface OnVibratorStateChangedListener  {
+        /**
+         * Called when the vibrator state has changed.
+         *
+         * @param isVibrating If true, the vibrator has started vibrating. If false,
+         *                    it's stopped vibrating.
+         */
+        void onVibratorStateChanged(boolean isVibrating);
+    }
+
+    /**
+     * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
+     * If the listener was previously added and not removed, this call will be ignored.
+     *
+     * @param listener listener to be added
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+    }
+
+    /**
+     * Adds a listener for vibrator state change. If the listener was previously added and not
+     * removed, this call will be ignored.
+     *
+     * @param listener listener to be added
+     * @param executor executor of listener
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public void addVibratorStateListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnVibratorStateChangedListener listener) {
+    }
+
+    /**
+     * Removes the listener for vibrator state changes. If the listener was not previously
+     * registered, this call will do nothing.
+     *
+     * @param listener listener to be removed
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+    }
+}
diff --git a/android-34/android/os/VibratorInfo.java b/android-34/android/os/VibratorInfo.java
new file mode 100644
index 0000000..71ec096
--- /dev/null
+++ b/android-34/android/os/VibratorInfo.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.vibrator.Braking;
+import android.hardware.vibrator.IVibrator;
+import android.util.MathUtils;
+import android.util.Range;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A VibratorInfo describes the capabilities of a {@link Vibrator}.
+ *
+ * <p>This description includes its capabilities, list of supported effects and composition
+ * primitives.
+ *
+ * @hide
+ */
+public class VibratorInfo implements Parcelable {
+    private static final String TAG = "VibratorInfo";
+
+    /** @hide */
+    public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build();
+
+    private final int mId;
+    private final long mCapabilities;
+    @Nullable
+    private final SparseBooleanArray mSupportedEffects;
+    @Nullable
+    private final SparseBooleanArray mSupportedBraking;
+    private final SparseIntArray mSupportedPrimitives;
+    private final int mPrimitiveDelayMax;
+    private final int mCompositionSizeMax;
+    private final int mPwlePrimitiveDurationMax;
+    private final int mPwleSizeMax;
+    private final float mQFactor;
+    private final FrequencyProfile mFrequencyProfile;
+
+    VibratorInfo(Parcel in) {
+        mId = in.readInt();
+        mCapabilities = in.readLong();
+        mSupportedEffects = in.readSparseBooleanArray();
+        mSupportedBraking = in.readSparseBooleanArray();
+        mSupportedPrimitives = in.readSparseIntArray();
+        mPrimitiveDelayMax = in.readInt();
+        mCompositionSizeMax = in.readInt();
+        mPwlePrimitiveDurationMax = in.readInt();
+        mPwleSizeMax = in.readInt();
+        mQFactor = in.readFloat();
+        mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
+    }
+
+    public VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo) {
+        this(id, baseVibratorInfo.mCapabilities, baseVibratorInfo.mSupportedEffects,
+                baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives,
+                baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
+                baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
+                baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfile);
+    }
+
+    /**
+     * Default constructor.
+     *
+     * @param id                       The vibrator id.
+     * @param capabilities             All capability flags of the vibrator, defined in
+     *                                 IVibrator.CAP_*.
+     * @param supportedEffects         All supported predefined effects, enum values from
+     *                                 {@link android.hardware.vibrator.Effect}.
+     * @param supportedBraking         All supported braking types, enum values from {@link
+     *                                 Braking}.
+     * @param supportedPrimitives      All supported primitive effects, key are enum values from
+     *                                 {@link android.hardware.vibrator.CompositePrimitive} and
+     *                                 values are estimated durations in milliseconds.
+     * @param primitiveDelayMax        The maximum delay that can be set to a composition primitive
+     *                                 in milliseconds.
+     * @param compositionSizeMax       The maximum number of primitives supported by a composition.
+     * @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds.
+     * @param pwleSizeMax              The maximum number of primitives supported by a PWLE
+     *                                 composition.
+     * @param qFactor                  The vibrator quality factor.
+     * @param frequencyProfile         The description of the vibrator supported frequencies and max
+     *                                 amplitude mappings.
+     * @hide
+     */
+    public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
+            @Nullable SparseBooleanArray supportedBraking,
+            @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
+            int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
+            float qFactor, @NonNull FrequencyProfile frequencyProfile) {
+        Preconditions.checkNotNull(supportedPrimitives);
+        Preconditions.checkNotNull(frequencyProfile);
+        mId = id;
+        mCapabilities = capabilities;
+        mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
+        mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
+        mSupportedPrimitives = supportedPrimitives.clone();
+        mPrimitiveDelayMax = primitiveDelayMax;
+        mCompositionSizeMax = compositionSizeMax;
+        mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+        mPwleSizeMax = pwleSizeMax;
+        mQFactor = qFactor;
+        mFrequencyProfile = frequencyProfile;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mId);
+        dest.writeLong(mCapabilities);
+        dest.writeSparseBooleanArray(mSupportedEffects);
+        dest.writeSparseBooleanArray(mSupportedBraking);
+        dest.writeSparseIntArray(mSupportedPrimitives);
+        dest.writeInt(mPrimitiveDelayMax);
+        dest.writeInt(mCompositionSizeMax);
+        dest.writeInt(mPwlePrimitiveDurationMax);
+        dest.writeInt(mPwleSizeMax);
+        dest.writeFloat(mQFactor);
+        mFrequencyProfile.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof VibratorInfo)) {
+            return false;
+        }
+        VibratorInfo that = (VibratorInfo) o;
+        int supportedPrimitivesCount = mSupportedPrimitives.size();
+        if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
+            return false;
+        }
+        for (int i = 0; i < supportedPrimitivesCount; i++) {
+            if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
+                return false;
+            }
+            if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
+                return false;
+            }
+        }
+        return mId == that.mId && mCapabilities == that.mCapabilities
+                && mPrimitiveDelayMax == that.mPrimitiveDelayMax
+                && mCompositionSizeMax == that.mCompositionSizeMax
+                && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
+                && mPwleSizeMax == that.mPwleSizeMax
+                && Objects.equals(mSupportedEffects, that.mSupportedEffects)
+                && Objects.equals(mSupportedBraking, that.mSupportedBraking)
+                && Objects.equals(mQFactor, that.mQFactor)
+                && Objects.equals(mFrequencyProfile, that.mFrequencyProfile);
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
+                mQFactor, mFrequencyProfile);
+        for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+            hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
+            hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
+        }
+        return hashCode;
+    }
+
+    @Override
+    public String toString() {
+        return "VibratorInfo{"
+                + "mId=" + mId
+                + ", mCapabilities=" + Arrays.toString(getCapabilitiesNames())
+                + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
+                + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
+                + ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
+                + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+                + ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
+                + ", mCompositionSizeMax=" + mCompositionSizeMax
+                + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
+                + ", mPwleSizeMax=" + mPwleSizeMax
+                + ", mQFactor=" + mQFactor
+                + ", mFrequencyProfile=" + mFrequencyProfile
+                + '}';
+    }
+
+    /** Return the id of this vibrator. */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Check whether the vibrator has amplitude control.
+     *
+     * @return True if the hardware can control the amplitude of the vibrations, otherwise false.
+     */
+    public boolean hasAmplitudeControl() {
+        return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
+    }
+
+    /**
+     * Returns a default value to be applied to composed PWLE effects for braking.
+     *
+     * @return a supported braking value, one of android.hardware.vibrator.Braking.*
+     * @hide
+     */
+    public int getDefaultBraking() {
+        if (mSupportedBraking != null) {
+            int size = mSupportedBraking.size();
+            for (int i = 0; i < size; i++) {
+                if (mSupportedBraking.keyAt(i) != Braking.NONE) {
+                    return mSupportedBraking.keyAt(i);
+                }
+            }
+        }
+        return Braking.NONE;
+    }
+
+    /** @hide */
+    @Nullable
+    public SparseBooleanArray getSupportedBraking() {
+        if (mSupportedBraking == null) {
+            return null;
+        }
+        return mSupportedBraking.clone();
+    }
+
+    /** @hide */
+    public boolean isBrakingSupportKnown() {
+        return mSupportedBraking != null;
+    }
+
+    /** @hide */
+    public boolean hasBrakingSupport(@Braking int braking) {
+        return (mSupportedBraking != null) && mSupportedBraking.get(braking);
+    }
+
+    /** @hide */
+    public boolean isEffectSupportKnown() {
+        return mSupportedEffects != null;
+    }
+
+    /**
+     * Query whether the vibrator supports the given effect.
+     *
+     * @param effectId Which effects to query for.
+     * @return {@link Vibrator#VIBRATION_EFFECT_SUPPORT_YES} if the effect is supported,
+     * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or
+     * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's
+     * supported or not.
+     */
+    @Vibrator.VibrationEffectSupport
+    public int isEffectSupported(@VibrationEffect.EffectType int effectId) {
+        if (mSupportedEffects == null) {
+            return Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+        }
+        return mSupportedEffects.get(effectId) ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES
+                : Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
+    }
+
+    /** @hide */
+    @Nullable
+    public SparseBooleanArray getSupportedEffects() {
+        if (mSupportedEffects == null) {
+            return null;
+        }
+        return mSupportedEffects.clone();
+    }
+
+    /**
+     * Query whether the vibrator supports the given primitive.
+     *
+     * @param primitiveId Which primitives to query for.
+     * @return Whether the primitive is supported.
+     */
+    public boolean isPrimitiveSupported(
+            @VibrationEffect.Composition.PrimitiveType int primitiveId) {
+        return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
+                && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
+    }
+
+    /**
+     * Query the estimated duration of given primitive.
+     *
+     * @param primitiveId Which primitives to query for.
+     * @return The duration in milliseconds estimated for the primitive, or zero if primitive not
+     * supported.
+     */
+    public int getPrimitiveDuration(
+            @VibrationEffect.Composition.PrimitiveType int primitiveId) {
+        return mSupportedPrimitives.get(primitiveId);
+    }
+
+    /** @hide */
+    public SparseIntArray getSupportedPrimitives() {
+        return mSupportedPrimitives.clone();
+    }
+
+    /**
+     * Query the maximum delay supported for a primitive in a composed effect.
+     *
+     * @return The max delay in milliseconds, or zero if unlimited.
+     */
+    public int getPrimitiveDelayMax() {
+        return mPrimitiveDelayMax;
+    }
+
+    /**
+     * Query the maximum number of primitives supported in a composed effect.
+     *
+     * @return The max number of primitives supported, or zero if unlimited.
+     */
+    public int getCompositionSizeMax() {
+        return mCompositionSizeMax;
+    }
+
+    /**
+     * Query the maximum duration supported for a primitive in a PWLE composition.
+     *
+     * @return The max duration in milliseconds, or zero if unlimited.
+     */
+    public int getPwlePrimitiveDurationMax() {
+        return mPwlePrimitiveDurationMax;
+    }
+
+    /**
+     * Query the maximum number of primitives supported in a PWLE composition.
+     *
+     * @return The max number of primitives supported, or zero if unlimited.
+     */
+    public int getPwleSizeMax() {
+        return mPwleSizeMax;
+    }
+
+    /**
+     * Check against this vibrator capabilities.
+     *
+     * @param capability one of IVibrator.CAP_*
+     * @return true if this vibrator has this capability, false otherwise
+     * @hide
+     */
+    public boolean hasCapability(long capability) {
+        return (mCapabilities & capability) == capability;
+    }
+
+    /**
+     * Gets the resonant frequency of the vibrator.
+     *
+     * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+     *         this vibrator is a composite of multiple physical devices.
+     */
+    public float getResonantFrequencyHz() {
+        return mFrequencyProfile.mResonantFrequencyHz;
+    }
+
+    /**
+     * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
+     *
+     * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
+     *         this vibrator is a composite of multiple physical devices.
+     */
+    public float getQFactor() {
+        return mQFactor;
+    }
+
+    /**
+     * Gets the profile of supported frequencies, including the measurements of maximum relative
+     * output acceleration for supported vibration frequencies.
+     *
+     * <p>If the devices does not have frequency control then the profile should be empty.
+     */
+    @NonNull
+    public FrequencyProfile getFrequencyProfile() {
+        return mFrequencyProfile;
+    }
+
+    protected long getCapabilities() {
+        return mCapabilities;
+    }
+
+    private String[] getCapabilitiesNames() {
+        List<String> names = new ArrayList<>();
+        if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
+            names.add("ON_CALLBACK");
+        }
+        if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
+            names.add("PERFORM_CALLBACK");
+        }
+        if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+            names.add("COMPOSE_EFFECTS");
+        }
+        if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+            names.add("COMPOSE_PWLE_EFFECTS");
+        }
+        if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
+            names.add("ALWAYS_ON_CONTROL");
+        }
+        if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
+            names.add("AMPLITUDE_CONTROL");
+        }
+        if (hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)) {
+            names.add("FREQUENCY_CONTROL");
+        }
+        if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+            names.add("EXTERNAL_CONTROL");
+        }
+        if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
+            names.add("EXTERNAL_AMPLITUDE_CONTROL");
+        }
+        return names.toArray(new String[names.size()]);
+    }
+
+    private String[] getSupportedEffectsNames() {
+        if (mSupportedEffects == null) {
+            return new String[0];
+        }
+        String[] names = new String[mSupportedEffects.size()];
+        for (int i = 0; i < mSupportedEffects.size(); i++) {
+            names[i] = VibrationEffect.effectIdToString(mSupportedEffects.keyAt(i));
+        }
+        return names;
+    }
+
+    private String[] getSupportedBrakingNames() {
+        if (mSupportedBraking == null) {
+            return new String[0];
+        }
+        String[] names = new String[mSupportedBraking.size()];
+        for (int i = 0; i < mSupportedBraking.size(); i++) {
+            switch (mSupportedBraking.keyAt(i)) {
+                case Braking.NONE:
+                    names[i] = "NONE";
+                    break;
+                case Braking.CLAB:
+                    names[i] = "CLAB";
+                    break;
+                default:
+                    names[i] = Integer.toString(mSupportedBraking.keyAt(i));
+            }
+        }
+        return names;
+    }
+
+    private String[] getSupportedPrimitivesNames() {
+        int supportedPrimitivesCount = mSupportedPrimitives.size();
+        String[] names = new String[supportedPrimitivesCount];
+        for (int i = 0; i < supportedPrimitivesCount; i++) {
+            names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i))
+                    + "(" + mSupportedPrimitives.valueAt(i) + "ms)";
+        }
+        return names;
+    }
+
+    /**
+     * Describes the maximum relative output acceleration that can be achieved for each supported
+     * frequency in a specific vibrator.
+     *
+     * <p>This profile is defined by the following parameters:
+     *
+     * <ol>
+     *     <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz}
+     *         provided by the vibrator in hertz.
+     *     <li>{@code maxAmplitudes} a list of values in [0,1] provided by the vibrator, where
+     *         {@code maxAmplitudes[i]} represents max supported amplitude at frequency
+     *         {@code minFrequencyHz + frequencyResolutionHz * i}.
+     *     <li>{@code maxFrequencyHz = minFrequencyHz
+     *                                     + frequencyResolutionHz * (maxAmplitudes.length-1)}
+     * </ol>
+     *
+     * @hide
+     */
+    public static final class FrequencyProfile implements Parcelable {
+        @Nullable
+        private final Range<Float> mFrequencyRangeHz;
+        private final float mMinFrequencyHz;
+        private final float mResonantFrequencyHz;
+        private final float mFrequencyResolutionHz;
+        private final float[] mMaxAmplitudes;
+
+        FrequencyProfile(Parcel in) {
+            this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray());
+        }
+
+        /**
+         * Default constructor.
+         *
+         * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
+         * @param minFrequencyHz        Minimum supported frequency, in hertz.
+         * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
+         *                              amplitude measurements.
+         * @param maxAmplitudes         The max amplitude supported by each supported frequency,
+         *                              starting at minimum frequency with jumps of frequency
+         *                              resolution.
+         * @hide
+         */
+        public FrequencyProfile(float resonantFrequencyHz, float minFrequencyHz,
+                float frequencyResolutionHz, float[] maxAmplitudes) {
+            mMinFrequencyHz = minFrequencyHz;
+            mResonantFrequencyHz = resonantFrequencyHz;
+            mFrequencyResolutionHz = frequencyResolutionHz;
+            mMaxAmplitudes = new float[maxAmplitudes == null ? 0 : maxAmplitudes.length];
+            if (maxAmplitudes != null) {
+                System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
+            }
+
+            // If any required field is undefined or has a bad value then this profile is invalid.
+            boolean isValid = !Float.isNaN(resonantFrequencyHz)
+                    && (resonantFrequencyHz > 0)
+                    && !Float.isNaN(minFrequencyHz)
+                    && (minFrequencyHz > 0)
+                    && !Float.isNaN(frequencyResolutionHz)
+                    && (frequencyResolutionHz > 0)
+                    && (mMaxAmplitudes.length > 0);
+
+            // If any max amplitude is outside the allowed range then this profile is invalid.
+            for (int i = 0; i < mMaxAmplitudes.length; i++) {
+                isValid &= (mMaxAmplitudes[i] >= 0) && (mMaxAmplitudes[i] <= 1);
+            }
+
+            float maxFrequencyHz = isValid
+                    ? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1)
+                    : Float.NaN;
+
+            // If the constraint min < resonant < max is not met then it is invalid.
+            isValid &= !Float.isNaN(maxFrequencyHz)
+                    && (resonantFrequencyHz >= minFrequencyHz)
+                    && (resonantFrequencyHz <= maxFrequencyHz)
+                    && (minFrequencyHz < maxFrequencyHz);
+
+            mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null;
+        }
+
+        /** Returns true if the supported frequency range is empty. */
+        public boolean isEmpty() {
+            return mFrequencyRangeHz == null;
+        }
+
+        /** Returns the supported frequency range, in hertz. */
+        @Nullable
+        public Range<Float> getFrequencyRangeHz() {
+            return mFrequencyRangeHz;
+        }
+
+        /**
+         * Returns the maximum relative amplitude the vibrator can reach while playing at the
+         * given frequency.
+         *
+         * @param frequencyHz frequency, in hertz, for query.
+         * @return A value in [0,1] representing the max relative amplitude supported at the given
+         * frequency. This will return 0 if the frequency is outside the supported range, or if the
+         * supported frequency range is empty.
+         */
+        public float getMaxAmplitude(float frequencyHz) {
+            if (isEmpty() || Float.isNaN(frequencyHz) || !mFrequencyRangeHz.contains(frequencyHz)) {
+                // Unsupported frequency requested, vibrator cannot play at this frequency.
+                return 0;
+            }
+
+            // Subtract minFrequencyHz to simplify offset calculations.
+            float mappingFreq = frequencyHz - mMinFrequencyHz;
+
+            // Find the bucket to interpolate within.
+            // Any calculated index should be safe, except exactly equal to max amplitude can be
+            // one step too high, so constrain it to guarantee safety.
+            int startIdx = MathUtils.constrain(
+                    /* amount= */ (int) Math.floor(mappingFreq / mFrequencyResolutionHz),
+                    /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
+            int nextIdx = MathUtils.constrain(
+                    /* amount= */ startIdx + 1,
+                    /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
+
+            // Linearly interpolate the amplitudes based on the frequency range of the bucket.
+            return MathUtils.constrainedMap(
+                    mMaxAmplitudes[startIdx], mMaxAmplitudes[nextIdx],
+                    startIdx * mFrequencyResolutionHz, nextIdx * mFrequencyResolutionHz,
+                    mappingFreq);
+        }
+
+        /** Returns the raw list of maximum relative output accelerations from the vibrator. */
+        @NonNull
+        public float[] getMaxAmplitudes() {
+            return Arrays.copyOf(mMaxAmplitudes, mMaxAmplitudes.length);
+        }
+
+        /** Returns the raw frequency resolution used for max amplitude measurements, in hertz. */
+        public float getFrequencyResolutionHz() {
+            return mFrequencyResolutionHz;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeFloat(mResonantFrequencyHz);
+            dest.writeFloat(mMinFrequencyHz);
+            dest.writeFloat(mFrequencyResolutionHz);
+            dest.writeFloatArray(mMaxAmplitudes);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof FrequencyProfile)) {
+                return false;
+            }
+            FrequencyProfile that = (FrequencyProfile) o;
+            return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
+                    && Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
+                    && Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
+                    && Arrays.equals(mMaxAmplitudes, that.mMaxAmplitudes);
+        }
+
+        @Override
+        public int hashCode() {
+            int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
+                    mFrequencyResolutionHz);
+            hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
+            return hashCode;
+        }
+
+        @Override
+        public String toString() {
+            return "FrequencyProfile{"
+                    + "mFrequencyRange=" + mFrequencyRangeHz
+                    + ", mMinFrequency=" + mMinFrequencyHz
+                    + ", mResonantFrequency=" + mResonantFrequencyHz
+                    + ", mFrequencyResolution=" + mFrequencyResolutionHz
+                    + ", mMaxAmplitudes count=" + mMaxAmplitudes.length
+                    + '}';
+        }
+
+        @NonNull
+        public static final Creator<FrequencyProfile> CREATOR =
+                new Creator<FrequencyProfile>() {
+                    @Override
+                    public FrequencyProfile createFromParcel(Parcel in) {
+                        return new FrequencyProfile(in);
+                    }
+
+                    @Override
+                    public FrequencyProfile[] newArray(int size) {
+                        return new FrequencyProfile[size];
+                    }
+                };
+    }
+
+    /** @hide */
+    public static final class Builder {
+        private final int mId;
+        private long mCapabilities;
+        private SparseBooleanArray mSupportedEffects;
+        private SparseBooleanArray mSupportedBraking;
+        private SparseIntArray mSupportedPrimitives = new SparseIntArray();
+        private int mPrimitiveDelayMax;
+        private int mCompositionSizeMax;
+        private int mPwlePrimitiveDurationMax;
+        private int mPwleSizeMax;
+        private float mQFactor = Float.NaN;
+        private FrequencyProfile mFrequencyProfile =
+                new FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
+
+        /** A builder class for a {@link VibratorInfo}. */
+        public Builder(int id) {
+            mId = id;
+        }
+
+        /** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
+        @NonNull
+        public Builder setCapabilities(long capabilities) {
+            mCapabilities = capabilities;
+            return this;
+        }
+
+        /** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
+        @NonNull
+        public Builder setSupportedEffects(int... supportedEffects) {
+            mSupportedEffects = toSparseBooleanArray(supportedEffects);
+            return this;
+        }
+
+        /** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
+        @NonNull
+        public Builder setSupportedBraking(int... supportedBraking) {
+            mSupportedBraking = toSparseBooleanArray(supportedBraking);
+            return this;
+        }
+
+        /** Configure maximum duration, in milliseconds, of a PWLE primitive. */
+        @NonNull
+        public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
+            mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+            return this;
+        }
+
+        /** Configure maximum number of primitives supported in a single PWLE composed effect. */
+        @NonNull
+        public Builder setPwleSizeMax(int pwleSizeMax) {
+            mPwleSizeMax = pwleSizeMax;
+            return this;
+        }
+
+        /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
+        @NonNull
+        public Builder setSupportedPrimitive(int primitiveId, int duration) {
+            mSupportedPrimitives.put(primitiveId, duration);
+            return this;
+        }
+
+        /** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */
+        @NonNull
+        public Builder setPrimitiveDelayMax(int primitiveDelayMax) {
+            mPrimitiveDelayMax = primitiveDelayMax;
+            return this;
+        }
+
+        /** Configure maximum number of primitives supported in a single composed effect. */
+        @NonNull
+        public Builder setCompositionSizeMax(int compositionSizeMax) {
+            mCompositionSizeMax = compositionSizeMax;
+            return this;
+        }
+
+        /** Configure the vibrator quality factor. */
+        @NonNull
+        public Builder setQFactor(float qFactor) {
+            mQFactor = qFactor;
+            return this;
+        }
+
+        /** Configure the vibrator frequency information like resonant frequency and bandwidth. */
+        @NonNull
+        public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) {
+            mFrequencyProfile = frequencyProfile;
+            return this;
+        }
+
+        /** Build the configured {@link VibratorInfo}. */
+        @NonNull
+        public VibratorInfo build() {
+            return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
+                    mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
+                    mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfile);
+        }
+
+        /**
+         * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is
+         * mapped
+         * to {@code true}.
+         */
+        @Nullable
+        private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
+            if (supportedKeys == null) {
+                return null;
+            }
+            SparseBooleanArray array = new SparseBooleanArray();
+            for (int key : supportedKeys) {
+                array.put(key, true);
+            }
+            return array;
+        }
+    }
+
+    @NonNull
+    public static final Creator<VibratorInfo> CREATOR =
+            new Creator<VibratorInfo>() {
+                @Override
+                public VibratorInfo createFromParcel(Parcel in) {
+                    return new VibratorInfo(in);
+                }
+
+                @Override
+                public VibratorInfo[] newArray(int size) {
+                    return new VibratorInfo[size];
+                }
+            };
+}
diff --git a/android-34/android/os/VibratorManager.java b/android-34/android/os/VibratorManager.java
new file mode 100644
index 0000000..f506ef8
--- /dev/null
+++ b/android-34/android/os/VibratorManager.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Provides access to all vibrators from the device, as well as the ability to run them
+ * in a synchronized fashion.
+ * <p>
+ * If your process exits, any vibration you started will stop.
+ * </p>
+ */
+@SystemService(Context.VIBRATOR_MANAGER_SERVICE)
+public abstract class VibratorManager {
+    private static final String TAG = "VibratorManager";
+
+    private final String mPackageName;
+
+    /**
+     * @hide to prevent subclassing from outside of the framework
+     */
+    public VibratorManager() {
+        mPackageName = ActivityThread.currentPackageName();
+    }
+
+    /**
+     * @hide to prevent subclassing from outside of the framework
+     */
+    protected VibratorManager(Context context) {
+        mPackageName = context.getOpPackageName();
+    }
+
+    /**
+     * List all available vibrator ids, returning a possible empty list.
+     *
+     * @return An array containing the ids of the vibrators available on the device.
+     */
+    @NonNull
+    public abstract int[] getVibratorIds();
+
+    /**
+     * Retrieve a single vibrator by id.
+     *
+     * @param vibratorId The id of the vibrator to be retrieved.
+     * @return The vibrator with given {@code vibratorId}, never null.
+     */
+    @NonNull
+    public abstract Vibrator getVibrator(int vibratorId);
+
+    /**
+     * Returns the default Vibrator for the device.
+     */
+    @NonNull
+    public abstract Vibrator getDefaultVibrator();
+
+    /**
+     * Configure an always-on haptics effect.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
+    public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+            @Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes) {
+        Log.w(TAG, "Always-on effects aren't supported");
+        return false;
+    }
+
+    /**
+     * Vibrate with a given combination of effects.
+     *
+     * <p>
+     * Pass in a {@link CombinedVibration} representing a combination of {@link
+     * VibrationEffect VibrationEffects} to be played on one or more vibrators.
+     * </p>
+     *
+     * <p>The app should be in foreground for the vibration to happen.</p>
+     *
+     * @param effect a combination of effects to be performed by one or more vibrators.
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public final void vibrate(@NonNull CombinedVibration effect) {
+        vibrate(effect, null);
+    }
+
+    /**
+     * Vibrate with a given combination of effects.
+     *
+     * <p>
+     * Pass in a {@link CombinedVibration} representing a combination of {@link
+     * VibrationEffect} to be played on one or more vibrators.
+     * </p>
+     *
+     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
+     * @param effect a combination of effects to be performed by one or more vibrators.
+     * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
+     *                   specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
+     *                   {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with
+     *                   incoming calls.
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public final void vibrate(@NonNull CombinedVibration effect,
+            @Nullable VibrationAttributes attributes) {
+        vibrate(Process.myUid(), mPackageName, effect, null, attributes);
+    }
+
+    /**
+     * Like {@link #vibrate(CombinedVibration, VibrationAttributes)}, but allows the
+     * caller to specify the vibration is owned by someone else and set reason for vibration.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public abstract void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
+            String reason, @Nullable VibrationAttributes attributes);
+
+    /**
+     * Turn all the vibrators off.
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public abstract void cancel();
+
+    /**
+     * Cancel specific types of ongoing vibrations.
+     *
+     * @param usageFilter The type of vibration to be cancelled, represented as a bitwise
+     *                    combination of {@link VibrationAttributes.Usage} values.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public abstract void cancel(int usageFilter);
+}
diff --git a/android-34/android/os/VibratorPerfTest.java b/android-34/android/os/VibratorPerfTest.java
new file mode 100644
index 0000000..0efe8cf
--- /dev/null
+++ b/android-34/android/os/VibratorPerfTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.content.Context;
+
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+@LargeTest
+public class VibratorPerfTest {
+    @Rule
+    public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
+
+    private Vibrator mVibrator;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mVibrator = context.getSystemService(Vibrator.class);
+    }
+
+    @Test
+    public void testEffectClick() {
+        final BenchmarkState state = mBenchmarkRule.getState();
+        while (state.keepRunning()) {
+            mVibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+        }
+    }
+
+    @Test
+    public void testOneShot() {
+        final BenchmarkState state = mBenchmarkRule.getState();
+        while (state.keepRunning()) {
+            mVibrator.vibrate(VibrationEffect.createOneShot(SECONDS.toMillis(2),
+                    VibrationEffect.DEFAULT_AMPLITUDE));
+        }
+    }
+
+    @Test
+    public void testWaveform() {
+        final BenchmarkState state = mBenchmarkRule.getState();
+        long[] timings = new long[]{SECONDS.toMillis(1), SECONDS.toMillis(2), SECONDS.toMillis(1)};
+        while (state.keepRunning()) {
+            mVibrator.vibrate(VibrationEffect.createWaveform(timings, -1));
+        }
+    }
+
+    @Test
+    public void testCompose() {
+        final BenchmarkState state = mBenchmarkRule.getState();
+        while (state.keepRunning()) {
+            mVibrator.vibrate(
+                    VibrationEffect.startComposition()
+                            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 100)
+                            .compose());
+        }
+    }
+
+    @Test
+    public void testAreEffectsSupported() {
+        final BenchmarkState state = mBenchmarkRule.getState();
+        int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK};
+        while (state.keepRunning()) {
+            mVibrator.areEffectsSupported(effects);
+        }
+    }
+
+    @Test
+    public void testArePrimitivesSupported() {
+        final BenchmarkState state = mBenchmarkRule.getState();
+        int[] primitives = new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK,
+                VibrationEffect.Composition.PRIMITIVE_TICK};
+        while (state.keepRunning()) {
+            mVibrator.arePrimitivesSupported(primitives);
+        }
+    }
+}
diff --git a/android-34/android/os/VintfObject.java b/android-34/android/os/VintfObject.java
new file mode 100644
index 0000000..1f11197
--- /dev/null
+++ b/android-34/android/os/VintfObject.java
@@ -0,0 +1,150 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.util.Slog;
+
+import java.util.Map;
+
+/**
+ * Java API for libvintf.
+ *
+ * @hide
+ */
+@TestApi
+public class VintfObject {
+
+    private static final String LOG_TAG = "VintfObject";
+
+    /**
+     * Slurps all device information (both manifests and both matrices)
+     * and report them.
+     * If any error in getting one of the manifests, it is not included in
+     * the list.
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String[] report();
+
+    /**
+     * Verify that the given metadata for an OTA package is compatible with
+     * this device.
+     *
+     * @param packageInfo a list of serialized form of HalManifest's /
+     * CompatibilityMatri'ces (XML).
+     * @return = 0 if success (compatible)
+     *         &gt; 0 if incompatible
+     *         &lt; 0 if any error (mount partition fails, illformed XML, etc.)
+     *
+     * @deprecated Checking compatibility against an OTA package is no longer
+     * supported because the format of VINTF metadata in the OTA package may not
+     * be recognized by the current system.
+     *
+     * <p>
+     * <ul>
+     * <li>This function always returns 0 for non-empty {@code packageInfo}.
+     * </li>
+     * <li>This function returns the result of {@link #verifyWithoutAvb} for
+     * null or empty {@code packageInfo}.</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @Deprecated
+    public static int verify(String[] packageInfo) {
+        if (packageInfo != null && packageInfo.length > 0) {
+            Slog.w(LOG_TAG, "VintfObject.verify() with non-empty packageInfo is deprecated. "
+                    + "Skipping compatibility checks for update package.");
+            return 0;
+        }
+        Slog.w(LOG_TAG, "VintfObject.verify() is deprecated. Call verifyWithoutAvb() instead.");
+        return verifyWithoutAvb();
+    }
+
+    /**
+     * Verify Vintf compatibility on the device without checking AVB
+     * (Android Verified Boot). It is useful to verify a running system
+     * image where AVB check is irrelevant.
+     *
+     * @return = 0 if success (compatible)
+     *         > 0 if incompatible
+     *         < 0 if any error (mount partition fails, illformed XML, etc.)
+     *
+     * @hide
+     */
+    public static native int verifyWithoutAvb();
+
+    /**
+     * @return a list of HAL names and versions that is supported by this
+     * device as stated in device and framework manifests. For example,
+     * ["[email protected]", "[email protected]",
+     *  "[email protected]"]. There are no duplicates.
+     *
+     * For AIDL HALs, the version is a single number
+     * (e.g. "android.hardware.light@1"). Historically, this API strips the
+     * version number for AIDL HALs (e.g. "android.hardware.light"). Users
+     * of this API must be able to handle both for backwards compatibility.
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String[] getHalNamesAndVersions();
+
+    /**
+     * @return the BOARD_SEPOLICY_VERS build flag available in device manifest.
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getSepolicyVersion();
+
+    /**
+     * @return the PLATFORM_SEPOLICY_VERSION build flag available in framework
+     * compatibility matrix.
+     *
+     * @hide
+     */
+    @TestApi
+    public static native @NonNull String getPlatformSepolicyVersion();
+
+    /**
+     * @return a list of VNDK snapshots supported by the framework, as
+     * specified in framework manifest. For example,
+     * [("27", ["libjpeg.so", "libbase.so"]),
+     *  ("28", ["libjpeg.so", "libbase.so"])]
+     *
+     * @hide
+     */
+    @TestApi
+    public static native Map<String, String[]> getVndkSnapshots();
+
+    /**
+     * @return Target Framework Compatibility Matrix (FCM) version, a number
+     * specified in the device manifest indicating the FCM version that the
+     * device manifest implements. Null if device manifest doesn't specify this
+     * number (for legacy devices).
+     *
+     * @hide
+     */
+    @TestApi
+    public static native Long getTargetFrameworkCompatibilityMatrixVersion();
+
+    private VintfObject() {}
+}
diff --git a/android-34/android/os/VintfRuntimeInfo.java b/android-34/android/os/VintfRuntimeInfo.java
new file mode 100644
index 0000000..f17039b
--- /dev/null
+++ b/android-34/android/os/VintfRuntimeInfo.java
@@ -0,0 +1,99 @@
+/*
+ * 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.os;
+
+import android.annotation.TestApi;
+
+/**
+ * Java API for ::android::vintf::RuntimeInfo. Methods return null / 0 on any error.
+ *
+ * @hide
+ */
+@TestApi
+public class VintfRuntimeInfo {
+
+    private VintfRuntimeInfo() {}
+
+    /**
+     * @return /sys/fs/selinux/policyvers, via security_policyvers() native call
+     *
+     * @hide
+     */
+    public static native long getKernelSepolicyVersion();
+    /**
+     * @return content of /proc/cpuinfo
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getCpuInfo();
+    /**
+     * @return os name extracted from uname() native call
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getOsName();
+    /**
+     * @return node name extracted from uname() native call
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getNodeName();
+    /**
+     * @return os release extracted from uname() native call
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getOsRelease();
+    /**
+     * @return os version extracted from uname() native call
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getOsVersion();
+    /**
+     * @return hardware id extracted from uname() native call
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getHardwareId();
+    /**
+     * @return kernel version extracted from uname() native call. Format is
+     * {@code x.y.z}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static native String getKernelVersion();
+    /**
+     * @return libavb version in OS. Format is {@code x.y}.
+     *
+     * @hide
+     */
+    public static native String getBootAvbVersion();
+    /**
+     * @return libavb version in bootloader. Format is {@code x.y}.
+     *
+     * @hide
+     */
+    public static native String getBootVbmetaAvbVersion();
+}
diff --git a/android-34/android/os/WakeLockStats.java b/android-34/android/os/WakeLockStats.java
new file mode 100644
index 0000000..05a7313
--- /dev/null
+++ b/android-34/android/os/WakeLockStats.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of wake lock stats.
+ *  @hide
+ */
+public final class WakeLockStats implements Parcelable {
+
+    /** @hide */
+    public static class WakeLock {
+        public final int uid;
+        @NonNull
+        public final String name;
+        public final int timesAcquired;
+        public final long totalTimeHeldMs;
+
+        /**
+         * Time in milliseconds that the lock has been held or 0 if not currently holding the lock
+         */
+        public final long timeHeldMs;
+
+        public WakeLock(int uid, @NonNull String name, int timesAcquired, long totalTimeHeldMs,
+                long timeHeldMs) {
+            this.uid = uid;
+            this.name = name;
+            this.timesAcquired = timesAcquired;
+            this.totalTimeHeldMs = totalTimeHeldMs;
+            this.timeHeldMs = timeHeldMs;
+        }
+
+        private WakeLock(Parcel in) {
+            uid = in.readInt();
+            name = in.readString();
+            timesAcquired = in.readInt();
+            totalTimeHeldMs = in.readLong();
+            timeHeldMs = in.readLong();
+        }
+
+        private void writeToParcel(Parcel out) {
+            out.writeInt(uid);
+            out.writeString(name);
+            out.writeInt(timesAcquired);
+            out.writeLong(totalTimeHeldMs);
+            out.writeLong(timeHeldMs);
+        }
+
+        @Override
+        public String toString() {
+            return "WakeLock{"
+                    + "uid=" + uid
+                    + ", name='" + name + '\''
+                    + ", timesAcquired=" + timesAcquired
+                    + ", totalTimeHeldMs=" + totalTimeHeldMs
+                    + ", timeHeldMs=" + timeHeldMs
+                    + '}';
+        }
+    }
+
+    private final List<WakeLock> mWakeLocks;
+
+    /** @hide **/
+    public WakeLockStats(@NonNull List<WakeLock> wakeLocks) {
+        mWakeLocks = wakeLocks;
+    }
+
+    @NonNull
+    public List<WakeLock> getWakeLocks() {
+        return mWakeLocks;
+    }
+
+    private WakeLockStats(Parcel in) {
+        final int size = in.readInt();
+        mWakeLocks = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            mWakeLocks.add(new WakeLock(in));
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        final int size = mWakeLocks.size();
+        out.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            WakeLock stats = mWakeLocks.get(i);
+            stats.writeToParcel(out);
+        }
+    }
+
+    @NonNull
+    public static final Creator<WakeLockStats> CREATOR =
+            new Creator<WakeLockStats>() {
+                public WakeLockStats createFromParcel(Parcel in) {
+                    return new WakeLockStats(in);
+                }
+
+                public WakeLockStats[] newArray(int size) {
+                    return new WakeLockStats[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "WakeLockStats " + mWakeLocks;
+    }
+}
diff --git a/android-34/android/os/WorkSource.java b/android-34/android/os/WorkSource.java
new file mode 100644
index 0000000..bc80c8b
--- /dev/null
+++ b/android-34/android/os/WorkSource.java
@@ -0,0 +1,1247 @@
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Describes the source of some work that may be done by someone else.
+ * Currently the public representation of what a work source is not
+ * defined; this is an opaque container.
+ */
+public class WorkSource implements Parcelable {
+    static final String TAG = "WorkSource";
+    static final boolean DEBUG = false;
+
+    @UnsupportedAppUsage
+    int mNum;
+
+    @UnsupportedAppUsage
+    @NonNull
+    int[] mUids = new int[0];
+
+    @UnsupportedAppUsage
+    @Nullable
+    String[] mNames;
+
+    private ArrayList<WorkChain> mChains;
+
+    /**
+     * Internal statics to avoid object allocations in some operations.
+     * The WorkSource object itself is not thread safe, but we need to
+     * hold sTmpWorkSource lock while working with these statics.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    static final WorkSource sTmpWorkSource = new WorkSource(0);
+    /**
+     * For returning newbie work from a modification operation.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    static WorkSource sNewbWork;
+    /**
+     * For returning gone work from a modification operation.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    static WorkSource sGoneWork;
+
+    /**
+     * Create an empty work source.
+     */
+    public WorkSource() {
+        mNum = 0;
+        mChains = null;
+    }
+
+    /**
+     * Create a new WorkSource that is a copy of an existing one.
+     * If <var>orig</var> is null, an empty WorkSource is created.
+     */
+    public WorkSource(WorkSource orig) {
+        if (orig == null) {
+            mNum = 0;
+            mChains = null;
+            return;
+        }
+        mNum = orig.mNum;
+        mUids = orig.mUids.clone();
+        mNames = orig.mNames != null ? orig.mNames.clone() : null;
+
+        if (orig.mChains != null) {
+            // Make a copy of all WorkChains that exist on |orig| since they are mutable.
+            mChains = new ArrayList<>(orig.mChains.size());
+            for (WorkChain chain : orig.mChains) {
+                mChains.add(new WorkChain(chain));
+            }
+        } else {
+            mChains = null;
+        }
+    }
+
+
+    /**
+     * Creates a work source with the given uid.
+     * @param uid the uid performing the work
+     * @hide
+     */
+    @SystemApi
+    public WorkSource(int uid) {
+        mNum = 1;
+        mUids = new int[] { uid, 0 };
+        mNames = null;
+        mChains = null;
+    }
+
+    /**
+     * Creates a work source with the given uid and package name.
+     * @param uid the uid performing the work
+     * @param packageName the package performing the work
+     * @hide
+     */
+    @SystemApi
+    public WorkSource(int uid, @NonNull String packageName) {
+        Objects.requireNonNull(packageName, "packageName can't be null");
+        mNum = 1;
+        mUids = new int[] { uid, 0 };
+        mNames = new String[] { packageName, null };
+        mChains = null;
+    }
+
+    @UnsupportedAppUsage
+    WorkSource(Parcel in) {
+        mNum = in.readInt();
+        mUids = Objects.requireNonNullElse(in.createIntArray(), new int[0]);
+        mNames = in.createStringArray();
+
+        int numChains = in.readInt();
+        if (numChains >= 0) {
+            mChains = new ArrayList<>(numChains);
+            in.readParcelableList(mChains, WorkChain.class.getClassLoader(), android.os.WorkSource.WorkChain.class);
+        } else {
+            mChains = null;
+        }
+    }
+
+    /**
+     * Whether system services should create {@code WorkChains} (wherever possible) in the place
+     * of flat UID lists.
+     *
+     * @hide
+     */
+    public static boolean isChainedBatteryAttributionEnabled(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, 0) == 1;
+    }
+
+    /**
+     * Returns the number of uids in this work source.
+     * @hide
+     */
+    @SystemApi
+    public int size() {
+        return mNum;
+    }
+
+    /**
+     * @deprecated use {{@link #getUid(int)}} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public int get(int index) {
+        return getUid(index);
+    }
+
+    /**
+     * Get the uid at the given index.
+     * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined.
+     * @hide
+     */
+    @SystemApi
+    public int getUid(int index) {
+        return mUids[index];
+    }
+
+    /**
+     * Return the UID to which this WorkSource should be attributed to, i.e, the UID that
+     * initiated the work and not the UID performing it. If the WorkSource has no UIDs, returns -1
+     * instead.
+     *
+     * @hide
+     */
+    public int getAttributionUid() {
+        if (isEmpty()) {
+            return -1;
+        }
+
+        return mNum > 0 ? mUids[0] : mChains.get(0).getAttributionUid();
+    }
+
+    /**
+     * @deprecated use {{@link #getPackageName(int)}} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public String getName(int index) {
+        return getPackageName(index);
+    }
+
+    /**
+     * Get the package name at the given index.
+     * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public String getPackageName(int index) {
+        return mNames != null ? mNames[index] : null;
+    }
+
+    /**
+     * Clear names from this WorkSource. Uids are left intact. WorkChains if any, are left
+     * intact.
+     *
+     * <p>Useful when combining with another WorkSource that doesn't have names.
+     */
+    private void clearNames() {
+        if (mNames != null) {
+            mNames = null;
+            // Clear out any duplicate uids now that we don't have names to disambiguate them.
+            int destIndex = 1;
+            int newNum = mNum;
+            for (int sourceIndex = 1; sourceIndex < mNum; sourceIndex++) {
+                if (mUids[sourceIndex] == mUids[sourceIndex - 1]) {
+                    newNum--;
+                } else {
+                    mUids[destIndex] = mUids[sourceIndex];
+                    destIndex++;
+                }
+            }
+            mNum = newNum;
+        }
+    }
+
+    /**
+     * Clear this WorkSource to be empty.
+     */
+    public void clear() {
+        mNum = 0;
+        if (mChains != null) {
+            mChains.clear();
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof WorkSource) {
+            WorkSource other = (WorkSource) o;
+
+            if (diff(other)) {
+                return false;
+            }
+
+            if (mChains != null && !mChains.isEmpty()) {
+                return mChains.equals(other.mChains);
+            } else {
+                return other.mChains == null || other.mChains.isEmpty();
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 0;
+        for (int i = 0; i < mNum; i++) {
+            result = ((result << 4) | (result >>> 28)) ^ mUids[i];
+        }
+        if (mNames != null) {
+            for (int i = 0; i < mNum; i++) {
+                result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
+            }
+        }
+
+        if (mChains != null) {
+            result = ((result << 4) | (result >>> 28)) ^ mChains.hashCode();
+        }
+
+        return result;
+    }
+
+    /**
+     * Compare this WorkSource with another.
+     * @param other The WorkSource to compare against.
+     * @return If there is a difference, true is returned.
+     */
+    // TODO: This is a public API so it cannot be renamed. Because it is used in several places,
+    // we keep its semantics the same and ignore any differences in WorkChains (if any).
+    public boolean diff(WorkSource other) {
+        int N = mNum;
+        if (N != other.mNum) {
+            return true;
+        }
+        final int[] uids1 = mUids;
+        final int[] uids2 = other.mUids;
+        final String[] names1 = mNames;
+        final String[] names2 = other.mNames;
+        for (int i=0; i<N; i++) {
+            if (uids1[i] != uids2[i]) {
+                return true;
+            }
+            if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Replace the current contents of this work source with the given
+     * work source.  If {@code other} is null, the current work source
+     * will be made empty.
+     */
+    public void set(WorkSource other) {
+        if (other == null) {
+            clear();
+            return;
+        }
+        mNum = other.mNum;
+        if (mUids.length >= mNum) { // this has more data than other
+            System.arraycopy(other.mUids, 0, mUids, 0, mNum);
+        } else {
+            mUids = other.mUids.clone();
+        }
+        if (other.mNames != null) {
+            if (mNames != null && mNames.length >= mNum) {
+                System.arraycopy(other.mNames, 0, mNames, 0, mNum);
+            } else {
+                mNames = other.mNames.clone();
+            }
+        } else {
+            mNames = null;
+        }
+
+        if (other.mChains != null) {
+            if (mChains != null) {
+                mChains.clear();
+            } else {
+                mChains = new ArrayList<>(other.mChains.size());
+            }
+
+            for (WorkChain chain : other.mChains) {
+                mChains.add(new WorkChain(chain));
+            }
+        }
+    }
+
+    /** @hide */
+    public void set(int uid) {
+        mNum = 1;
+        if (mUids.length == 0) mUids = new int[2];
+        mUids[0] = uid;
+        mNames = null;
+        if (mChains != null) {
+            mChains.clear();
+        }
+    }
+
+    /** @hide */
+    public void set(int uid, String name) {
+        if (name == null) {
+            throw new NullPointerException("Name can't be null");
+        }
+        mNum = 1;
+        if (mUids.length == 0) {
+            mUids = new int[2];
+            mNames = new String[2];
+        }
+        mUids[0] = uid;
+        mNames[0] = name;
+        if (mChains != null) {
+            mChains.clear();
+        }
+    }
+
+    /**
+     * Legacy API, DO NOT USE: Only deals with flat UIDs and tags. No chains are transferred, and no
+     * differences in chains are returned. This will be removed once its callers have been
+     * rewritten.
+     *
+     * NOTE: This is currently only used in GnssLocationProvider.
+     *
+     * @hide
+     * @deprecated for internal use only. WorkSources are opaque and no external callers should need
+     *     to be aware of internal differences.
+     */
+    @Deprecated
+    @TestApi
+    public WorkSource[] setReturningDiffs(WorkSource other) {
+        synchronized (sTmpWorkSource) {
+            sNewbWork = null;
+            sGoneWork = null;
+            updateLocked(other, true, true);
+            if (sNewbWork != null || sGoneWork != null) {
+                WorkSource[] diffs = new WorkSource[2];
+                diffs[0] = sNewbWork;
+                diffs[1] = sGoneWork;
+                return diffs;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Merge the contents of <var>other</var> WorkSource in to this one.
+     *
+     * @param other The other WorkSource whose contents are to be merged.
+     * @return Returns true if any new sources were added.
+     */
+    public boolean add(WorkSource other) {
+        synchronized (sTmpWorkSource) {
+            boolean uidAdded = updateLocked(other, false, false);
+
+            boolean chainAdded = false;
+            if (other.mChains != null) {
+                // NOTE: This is quite an expensive operation, especially if the number of chains
+                // is large. We could look into optimizing it if it proves problematic.
+                if (mChains == null) {
+                    mChains = new ArrayList<>(other.mChains.size());
+                }
+
+                for (WorkChain wc : other.mChains) {
+                    if (!mChains.contains(wc)) {
+                        mChains.add(new WorkChain(wc));
+                        chainAdded = true;
+                    }
+                }
+            }
+
+            return uidAdded || chainAdded;
+        }
+    }
+
+    /**
+     * Returns a copy of this work source without any package names.
+     * If any {@link WorkChain WorkChains} are present, they are left intact.
+     *
+     * @return a {@link WorkSource} without any package names.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public WorkSource withoutNames() {
+        final WorkSource copy = new WorkSource(this);
+        copy.clearNames();
+        return copy;
+    }
+
+    /**
+     * Legacy API: DO NOT USE. Only in use from unit tests.
+     *
+     * @hide
+     * @deprecated meant for unit testing use only. Will be removed in a future API revision.
+     */
+    @Deprecated
+    @TestApi
+    public WorkSource addReturningNewbs(WorkSource other) {
+        synchronized (sTmpWorkSource) {
+            sNewbWork = null;
+            updateLocked(other, false, true);
+            return sNewbWork;
+        }
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    @TestApi
+    public boolean add(int uid) {
+        if (mNum <= 0) {
+            mNames = null;
+            insert(0, uid);
+            return true;
+        }
+        if (mNames != null) {
+            throw new IllegalArgumentException("Adding without name to named " + this);
+        }
+        int i = Arrays.binarySearch(mUids, 0, mNum, uid);
+        if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
+        if (i >= 0) {
+            return false;
+        }
+        insert(-i-1, uid);
+        return true;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    @TestApi
+    public boolean add(int uid, String name) {
+        if (mNum <= 0) {
+            insert(0, uid, name);
+            return true;
+        }
+        if (mNames == null) {
+            throw new IllegalArgumentException("Adding name to unnamed " + this);
+        }
+        int i;
+        for (i=0; i<mNum; i++) {
+            if (mUids[i] > uid) {
+                break;
+            }
+            if (mUids[i] == uid) {
+                int diff = mNames[i].compareTo(name);
+                if (diff > 0) {
+                    break;
+                }
+                if (diff == 0) {
+                    return false;
+                }
+            }
+        }
+        insert(i, uid, name);
+        return true;
+    }
+
+    public boolean remove(WorkSource other) {
+        if (isEmpty() || other.isEmpty()) {
+            return false;
+        }
+
+        boolean uidRemoved;
+        if (mNames == null && other.mNames == null) {
+            uidRemoved = removeUids(other);
+        } else {
+            if (mNames == null) {
+                throw new IllegalArgumentException("Other " + other + " has names, but target "
+                        + this + " does not");
+            }
+            if (other.mNames == null) {
+                throw new IllegalArgumentException("Target " + this + " has names, but other "
+                        + other + " does not");
+            }
+            uidRemoved = removeUidsAndNames(other);
+        }
+
+        boolean chainRemoved = false;
+        if (other.mChains != null && mChains != null) {
+            chainRemoved = mChains.removeAll(other.mChains);
+        }
+
+        return uidRemoved || chainRemoved;
+    }
+
+    /**
+     * Create a new {@code WorkChain} associated with this WorkSource and return it.
+     *
+     * @hide
+     */
+    @SystemApi
+    public WorkChain createWorkChain() {
+        if (mChains == null) {
+            mChains = new ArrayList<>(4);
+        }
+
+        final WorkChain wc = new WorkChain();
+        mChains.add(wc);
+
+        return wc;
+    }
+
+    /**
+     * Returns {@code true} iff. this work source contains zero UIDs and zero WorkChains to
+     * attribute usage to.
+     *
+     * @hide for internal use only.
+     */
+    @SystemApi
+    public boolean isEmpty() {
+        return mNum == 0 && (mChains == null || mChains.isEmpty());
+    }
+
+    /**
+     * @return the list of {@code WorkChains} associated with this {@code WorkSource}.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public List<WorkChain> getWorkChains() {
+        return mChains;
+    }
+
+    /**
+     * DO NOT USE: Hacky API provided solely for {@code GnssLocationProvider}. See
+     * {@code setReturningDiffs} as well.
+     *
+     * @hide
+     */
+    public void transferWorkChains(WorkSource other) {
+        if (mChains != null) {
+            mChains.clear();
+        }
+
+        if (other.mChains == null || other.mChains.isEmpty()) {
+            return;
+        }
+
+        if (mChains == null) {
+            mChains = new ArrayList<>(4);
+        }
+
+        mChains.addAll(other.mChains);
+        other.mChains.clear();
+    }
+
+    private boolean removeUids(WorkSource other) {
+        int N1 = mNum;
+        final int[] uids1 = mUids;
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        boolean changed = false;
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
+        while (i1 < N1 && i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
+                    + " of " + N2);
+            if (uids2[i2] == uids1[i1]) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
+                        + ": remove " + uids1[i1]);
+                N1--;
+                changed = true;
+                if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
+                i2++;
+            } else if (uids2[i2] > uids1[i1]) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
+                i1++;
+            } else {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
+                i2++;
+            }
+        }
+
+        mNum = N1;
+
+        return changed;
+    }
+
+    private boolean removeUidsAndNames(WorkSource other) {
+        int N1 = mNum;
+        final int[] uids1 = mUids;
+        final String[] names1 = mNames;
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        final String[] names2 = other.mNames;
+        boolean changed = false;
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
+        while (i1 < N1 && i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
+                    + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
+            if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
+                        + ": remove " + uids1[i1] + " " + names1[i1]);
+                N1--;
+                changed = true;
+                if (i1 < N1) {
+                    System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
+                    System.arraycopy(names1, i1+1, names1, i1, N1-i1);
+                }
+                i2++;
+            } else if (uids2[i2] > uids1[i1]
+                    || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
+                i1++;
+            } else {
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
+                i2++;
+            }
+        }
+
+        mNum = N1;
+
+        return changed;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
+        if (mNames == null && other.mNames == null) {
+            return updateUidsLocked(other, set, returnNewbs);
+        } else {
+            if (mNum > 0 && mNames == null) {
+                throw new IllegalArgumentException("Other " + other + " has names, but target "
+                        + this + " does not");
+            }
+            if (other.mNum > 0 && other.mNames == null) {
+                throw new IllegalArgumentException("Target " + this + " has names, but other "
+                        + other + " does not");
+            }
+            return updateUidsAndNamesLocked(other, set, returnNewbs);
+        }
+    }
+
+    private static WorkSource addWork(WorkSource cur, int newUid) {
+        if (cur == null) {
+            return new WorkSource(newUid);
+        }
+        cur.insert(cur.mNum, newUid);
+        return cur;
+    }
+
+    private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
+        int N1 = mNum;
+        int[] uids1 = mUids;
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        boolean changed = false;
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
+                + " returnNewbs=" + returnNewbs);
+        while (i1 < N1 || i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
+                    + " of " + N2);
+            if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
+                // Need to insert a new uid.
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
+                        + ": insert " + uids2[i2]);
+                changed = true;
+                if (uids1.length == 0) {
+                    uids1 = new int[4];
+                    uids1[0] = uids2[i2];
+                } else if (N1 >= uids1.length) {
+                    int[] newuids = new int[(uids1.length*3)/2];
+                    if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
+                    if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
+                    uids1 = newuids;
+                    uids1[i1] = uids2[i2];
+                } else {
+                    if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
+                    uids1[i1] = uids2[i2];
+                }
+                if (returnNewbs) {
+                    sNewbWork = addWork(sNewbWork, uids2[i2]);
+                }
+                N1++;
+                i1++;
+                i2++;
+            } else {
+                if (!set) {
+                    // Skip uids that already exist or are not in 'other'.
+                    if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
+                    if (i2 < N2 && uids2[i2] == uids1[i1]) {
+                        i2++;
+                    }
+                    i1++;
+                } else {
+                    // Remove any uids that don't exist in 'other'.
+                    int start = i1;
+                    while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
+                        sGoneWork = addWork(sGoneWork, uids1[i1]);
+                        i1++;
+                    }
+                    if (start < i1) {
+                        System.arraycopy(uids1, i1, uids1, start, N1-i1);
+                        N1 -= i1-start;
+                        i1 = start;
+                    }
+                    // If there is a matching uid, skip it.
+                    if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
+                        i1++;
+                        i2++;
+                    }
+                }
+            }
+        }
+
+        mNum = N1;
+        mUids = uids1;
+
+        return changed;
+    }
+
+    /**
+     * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
+     */
+    private int compare(WorkSource other, int i1, int i2) {
+        final int diff = mUids[i1] - other.mUids[i2];
+        if (diff != 0) {
+            return diff;
+        }
+        return mNames[i1].compareTo(other.mNames[i2]);
+    }
+
+    private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
+        if (cur == null) {
+            return new WorkSource(newUid, newName);
+        }
+        cur.insert(cur.mNum, newUid, newName);
+        return cur;
+    }
+
+    private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
+        final int N2 = other.mNum;
+        final int[] uids2 = other.mUids;
+        String[] names2 = other.mNames;
+        boolean changed = false;
+        int i1 = 0, i2 = 0;
+        if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
+                + " returnNewbs=" + returnNewbs);
+        while (i1 < mNum || i2 < N2) {
+            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
+                    + " of " + N2);
+            int diff = -1;
+            if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
+                // Need to insert a new uid.
+                changed = true;
+                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
+                        + ": insert " + uids2[i2] + " " + names2[i2]);
+                insert(i1, uids2[i2], names2[i2]);
+                if (returnNewbs) {
+                    sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
+                }
+                i1++;
+                i2++;
+            } else {
+                if (!set) {
+                    if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
+                    if (i2 < N2 && diff == 0) {
+                        i2++;
+                    }
+                    i1++;
+                } else {
+                    // Remove any uids that don't exist in 'other'.
+                    int start = i1;
+                    while (diff < 0) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
+                                + " " + mNames[i1]);
+                        sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
+                        i1++;
+                        if (i1 >= mNum) {
+                            break;
+                        }
+                        diff = i2 < N2 ? compare(other, i1, i2) : -1;
+                    }
+                    if (start < i1) {
+                        System.arraycopy(mUids, i1, mUids, start, mNum-i1);
+                        System.arraycopy(mNames, i1, mNames, start, mNum-i1);
+                        mNum -= i1-start;
+                        i1 = start;
+                    }
+                    // If there is a matching uid, skip it.
+                    if (i1 < mNum && diff == 0) {
+                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
+                        i1++;
+                        i2++;
+                    }
+                }
+            }
+        }
+
+        return changed;
+    }
+
+    private void insert(int index, int uid)  {
+        if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
+        if (mUids.length == 0) {
+            mUids = new int[4];
+            mUids[0] = uid;
+            mNum = 1;
+        } else if (mNum >= mUids.length) {
+            int[] newuids = new int[(mNum*3)/2];
+            if (index > 0) {
+                System.arraycopy(mUids, 0, newuids, 0, index);
+            }
+            if (index < mNum) {
+                System.arraycopy(mUids, index, newuids, index+1, mNum-index);
+            }
+            mUids = newuids;
+            mUids[index] = uid;
+            mNum++;
+        } else {
+            if (index < mNum) {
+                System.arraycopy(mUids, index, mUids, index+1, mNum-index);
+            }
+            mUids[index] = uid;
+            mNum++;
+        }
+    }
+
+    private void insert(int index, int uid, String name)  {
+        if (mNum == 0) {
+            mUids = new int[4];
+            mUids[0] = uid;
+            mNames = new String[4];
+            mNames[0] = name;
+            mNum = 1;
+        } else if (mNum >= mUids.length) {
+            int[] newuids = new int[(mNum*3)/2];
+            String[] newnames = new String[(mNum*3)/2];
+            if (index > 0) {
+                System.arraycopy(mUids, 0, newuids, 0, index);
+                System.arraycopy(mNames, 0, newnames, 0, index);
+            }
+            if (index < mNum) {
+                System.arraycopy(mUids, index, newuids, index+1, mNum-index);
+                System.arraycopy(mNames, index, newnames, index+1, mNum-index);
+            }
+            mUids = newuids;
+            mNames = newnames;
+            mUids[index] = uid;
+            mNames[index] = name;
+            mNum++;
+        } else {
+            if (index < mNum) {
+                System.arraycopy(mUids, index, mUids, index+1, mNum-index);
+                System.arraycopy(mNames, index, mNames, index+1, mNum-index);
+            }
+            mUids[index] = uid;
+            mNames[index] = name;
+            mNum++;
+        }
+    }
+
+    /**
+     * Represents an attribution chain for an item of work being performed. An attribution chain is
+     * an indexed list of {code (uid, tag)} nodes. The node at {@code index == 0} is the initiator
+     * of the work, and the node at the highest index performs the work. Nodes at other indices
+     * are intermediaries that facilitate the work. Examples :
+     *
+     * (1) Work being performed by uid=2456 (no chaining):
+     * <pre>
+     * WorkChain {
+     *   mUids = { 2456 }
+     *   mTags = { null }
+     *   mSize = 1;
+     * }
+     * </pre>
+     *
+     * (2) Work being performed by uid=2456 (from component "c1") on behalf of uid=5678:
+     *
+     * <pre>
+     * WorkChain {
+     *   mUids = { 5678, 2456 }
+     *   mTags = { null, "c1" }
+     *   mSize = 1
+     * }
+     * </pre>
+     *
+     * Attribution chains are mutable, though the only operation that can be performed on them
+     * is the addition of a new node at the end of the attribution chain to represent
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class WorkChain implements Parcelable {
+        private int mSize;
+        private int[] mUids;
+        private String[] mTags;
+
+        // @VisibleForTesting
+        public WorkChain() {
+            mSize = 0;
+            mUids = new int[4];
+            mTags = new String[4];
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public WorkChain(WorkChain other) {
+            mSize = other.mSize;
+            mUids = other.mUids.clone();
+            mTags = other.mTags.clone();
+        }
+
+        private WorkChain(Parcel in) {
+            mSize = in.readInt();
+            mUids = in.createIntArray();
+            mTags = in.createStringArray();
+        }
+
+        /**
+         * Append a node whose uid is {@code uid} and whose optional tag is {@code tag} to this
+         * {@code WorkChain}.
+         */
+        public WorkChain addNode(int uid, @Nullable String tag) {
+            if (mSize == mUids.length) {
+                resizeArrays();
+            }
+
+            mUids[mSize] = uid;
+            mTags[mSize] = tag;
+            mSize++;
+
+            return this;
+        }
+
+        /**
+         * Return the UID to which this WorkChain should be attributed to, i.e, the UID that
+         * initiated the work and not the UID performing it. Returns -1 if the chain is empty.
+         */
+        public int getAttributionUid() {
+            return mSize > 0 ? mUids[0] : -1;
+        }
+
+        /**
+         * Return the tag associated with the attribution UID. See (@link #getAttributionUid}.
+         * Returns null if the chain is empty.
+         */
+        public String getAttributionTag() {
+            return mTags.length > 0 ? mTags[0] : null;
+        }
+
+        // TODO: The following three trivial getters are purely for testing and will be removed
+        // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
+        // diffing it etc.
+
+
+        /** @hide */
+        @VisibleForTesting
+        public int[] getUids() {
+            int[] uids = new int[mSize];
+            System.arraycopy(mUids, 0, uids, 0, mSize);
+            return uids;
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public String[] getTags() {
+            String[] tags = new String[mSize];
+            System.arraycopy(mTags, 0, tags, 0, mSize);
+            return tags;
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public int getSize() {
+            return mSize;
+        }
+
+        private void resizeArrays() {
+            final int newSize = mSize * 2;
+            int[] uids = new int[newSize];
+            String[] tags = new String[newSize];
+
+            System.arraycopy(mUids, 0, uids, 0, mSize);
+            System.arraycopy(mTags, 0, tags, 0, mSize);
+
+            mUids = uids;
+            mTags = tags;
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            StringBuilder result = new StringBuilder("WorkChain{");
+            for (int i = 0; i < mSize; ++i) {
+                if (i != 0) {
+                    result.append(", ");
+                }
+                result.append("(");
+                result.append(mUids[i]);
+                if (mTags[i] != null) {
+                    result.append(", ");
+                    result.append(mTags[i]);
+                }
+                result.append(")");
+            }
+
+            result.append("}");
+            return result.toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return (mSize + 31 * Arrays.hashCode(mUids)) * 31 + Arrays.hashCode(mTags);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (o instanceof WorkChain) {
+                WorkChain other = (WorkChain) o;
+
+                return mSize == other.mSize
+                    && Arrays.equals(mUids, other.mUids)
+                    && Arrays.equals(mTags, other.mTags);
+            }
+
+            return false;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mSize);
+            dest.writeIntArray(mUids);
+            dest.writeStringArray(mTags);
+        }
+
+        public static final @android.annotation.NonNull Parcelable.Creator<WorkChain> CREATOR =
+                new Parcelable.Creator<WorkChain>() {
+                    public WorkChain createFromParcel(Parcel in) {
+                        return new WorkChain(in);
+                    }
+                    public WorkChain[] newArray(int size) {
+                        return new WorkChain[size];
+                    }
+                };
+    }
+
+    /**
+     * Computes the differences in WorkChains contained between {@code oldWs} and {@code newWs}.
+     *
+     * Returns {@code null} if no differences exist, otherwise returns a two element array. The
+     * first element is a list of "new" chains, i.e WorkChains present in {@code newWs} but not in
+     * {@code oldWs}. The second element is a list of "gone" chains, i.e WorkChains present in
+     * {@code oldWs} but not in {@code newWs}.
+     *
+     * @hide
+     */
+    public static ArrayList<WorkChain>[] diffChains(WorkSource oldWs, WorkSource newWs) {
+        ArrayList<WorkChain> newChains = null;
+        ArrayList<WorkChain> goneChains = null;
+
+        // TODO(narayan): This is a naive O(M*N) algorithm that determines what has changed across
+        // WorkSource objects. We can replace this with something smarter, for e.g by defining
+        // a Comparator between WorkChains. It's unclear whether that will be more efficient if
+        // the number of chains associated with a WorkSource is expected to be small
+        if (oldWs.mChains != null) {
+            for (int i = 0; i < oldWs.mChains.size(); ++i) {
+                final WorkChain wc = oldWs.mChains.get(i);
+                if (newWs.mChains == null || !newWs.mChains.contains(wc)) {
+                    if (goneChains == null) {
+                        goneChains = new ArrayList<>(oldWs.mChains.size());
+                    }
+                    goneChains.add(wc);
+                }
+            }
+        }
+
+        if (newWs.mChains != null) {
+            for (int i = 0; i < newWs.mChains.size(); ++i) {
+                final WorkChain wc = newWs.mChains.get(i);
+                if (oldWs.mChains == null || !oldWs.mChains.contains(wc)) {
+                    if (newChains == null) {
+                        newChains = new ArrayList<>(newWs.mChains.size());
+                    }
+                    newChains.add(wc);
+                }
+            }
+        }
+
+        if (newChains != null || goneChains != null) {
+            return new ArrayList[] { newChains, goneChains };
+        }
+
+        return null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mNum);
+        dest.writeIntArray(mUids);
+        dest.writeStringArray(mNames);
+
+        if (mChains == null) {
+            dest.writeInt(-1);
+        } else {
+            dest.writeInt(mChains.size());
+            dest.writeParcelableList(mChains, flags);
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("WorkSource{");
+        for (int i = 0; i < mNum; i++) {
+            if (i != 0) {
+                result.append(", ");
+            }
+            result.append(mUids[i]);
+            if (mNames != null) {
+                result.append(" ");
+                result.append(mNames[i]);
+            }
+        }
+
+        if (mChains != null) {
+            result.append(" chains=");
+            for (int i = 0; i < mChains.size(); ++i) {
+                if (i != 0) {
+                    result.append(", ");
+                }
+                result.append(mChains.get(i));
+            }
+        }
+
+        result.append("}");
+        return result.toString();
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long workSourceToken = proto.start(fieldId);
+        for (int i = 0; i < mNum; i++) {
+            final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
+            proto.write(WorkSourceProto.WorkSourceContentProto.UID, mUids[i]);
+            if (mNames != null) {
+                proto.write(WorkSourceProto.WorkSourceContentProto.NAME, mNames[i]);
+            }
+            proto.end(contentProto);
+        }
+
+        if (mChains != null) {
+            for (int i = 0; i < mChains.size(); i++) {
+                final WorkChain wc = mChains.get(i);
+                final long workChain = proto.start(WorkSourceProto.WORK_CHAINS);
+
+                final String[] tags = wc.getTags();
+                final int[] uids = wc.getUids();
+                for (int j = 0; j < tags.length; j++) {
+                    final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
+                    proto.write(WorkSourceProto.WorkSourceContentProto.UID, uids[j]);
+                    proto.write(WorkSourceProto.WorkSourceContentProto.NAME, tags[j]);
+                    proto.end(contentProto);
+                }
+
+                proto.end(workChain);
+            }
+        }
+
+        proto.end(workSourceToken);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<WorkSource> CREATOR = new Parcelable.Creator<>() {
+        public WorkSource createFromParcel(Parcel in) {
+            return new WorkSource(in);
+        }
+        public WorkSource[] newArray(int size) {
+            return new WorkSource[size];
+        }
+    };
+}
diff --git a/android-34/android/os/ZygoteProcess.java b/android-34/android/os/ZygoteProcess.java
new file mode 100644
index 0000000..3cb5c60
--- /dev/null
+++ b/android-34/android/os/ZygoteProcess.java
@@ -0,0 +1,1317 @@
+/*
+ * 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.os;
+
+import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ApplicationInfo;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.Zygote;
+import com.android.internal.os.ZygoteConfig;
+
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/*package*/ class ZygoteStartFailedEx extends Exception {
+    @UnsupportedAppUsage
+    ZygoteStartFailedEx(String s) {
+        super(s);
+    }
+
+    @UnsupportedAppUsage
+    ZygoteStartFailedEx(Throwable cause) {
+        super(cause);
+    }
+
+    ZygoteStartFailedEx(String s, Throwable cause) {
+        super(s, cause);
+    }
+}
+
+/**
+ * Maintains communication state with the zygote processes. This class is responsible
+ * for the sockets opened to the zygotes and for starting processes on behalf of the
+ * {@link android.os.Process} class.
+ *
+ * {@hide}
+ */
+public class ZygoteProcess {
+
+    private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000;
+
+    /**
+     * Use a relatively short delay, because for app zygote, this is in the critical path of
+     * service launch.
+     */
+    private static final int ZYGOTE_CONNECT_RETRY_DELAY_MS = 50;
+
+    private static final String LOG_TAG = "ZygoteProcess";
+
+    /**
+     * The name of the socket used to communicate with the primary zygote.
+     */
+    private final LocalSocketAddress mZygoteSocketAddress;
+
+    /**
+     * The name of the secondary (alternate ABI) zygote socket.
+     */
+    private final LocalSocketAddress mZygoteSecondarySocketAddress;
+
+    /**
+     * The name of the socket used to communicate with the primary USAP pool.
+     */
+    private final LocalSocketAddress mUsapPoolSocketAddress;
+
+    /**
+     * The name of the socket used to communicate with the secondary (alternate ABI) USAP pool.
+     */
+    private final LocalSocketAddress mUsapPoolSecondarySocketAddress;
+
+    public ZygoteProcess() {
+        mZygoteSocketAddress =
+                new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME,
+                                       LocalSocketAddress.Namespace.RESERVED);
+        mZygoteSecondarySocketAddress =
+                new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME,
+                                       LocalSocketAddress.Namespace.RESERVED);
+
+        mUsapPoolSocketAddress =
+                new LocalSocketAddress(Zygote.USAP_POOL_PRIMARY_SOCKET_NAME,
+                                       LocalSocketAddress.Namespace.RESERVED);
+        mUsapPoolSecondarySocketAddress =
+                new LocalSocketAddress(Zygote.USAP_POOL_SECONDARY_SOCKET_NAME,
+                                       LocalSocketAddress.Namespace.RESERVED);
+
+        // This constructor is used to create the primary and secondary Zygotes, which can support
+        // Unspecialized App Process Pools.
+        mUsapPoolSupported = true;
+    }
+
+    public ZygoteProcess(LocalSocketAddress primarySocketAddress,
+                         LocalSocketAddress secondarySocketAddress) {
+        mZygoteSocketAddress = primarySocketAddress;
+        mZygoteSecondarySocketAddress = secondarySocketAddress;
+
+        mUsapPoolSocketAddress = null;
+        mUsapPoolSecondarySocketAddress = null;
+
+        // This constructor is used to create the primary and secondary Zygotes, which CAN NOT
+        // support Unspecialized App Process Pools.
+        mUsapPoolSupported = false;
+    }
+
+    public LocalSocketAddress getPrimarySocketAddress() {
+        return mZygoteSocketAddress;
+    }
+
+    /**
+     * State for communicating with the zygote process.
+     */
+    private static class ZygoteState implements AutoCloseable {
+        final LocalSocketAddress mZygoteSocketAddress;
+        final LocalSocketAddress mUsapSocketAddress;
+
+        private final LocalSocket mZygoteSessionSocket;
+
+        final DataInputStream mZygoteInputStream;
+        final BufferedWriter mZygoteOutputWriter;
+
+        private final List<String> mAbiList;
+
+        private boolean mClosed;
+
+        private ZygoteState(LocalSocketAddress zygoteSocketAddress,
+                            LocalSocketAddress usapSocketAddress,
+                            LocalSocket zygoteSessionSocket,
+                            DataInputStream zygoteInputStream,
+                            BufferedWriter zygoteOutputWriter,
+                            List<String> abiList) {
+            this.mZygoteSocketAddress = zygoteSocketAddress;
+            this.mUsapSocketAddress = usapSocketAddress;
+            this.mZygoteSessionSocket = zygoteSessionSocket;
+            this.mZygoteInputStream = zygoteInputStream;
+            this.mZygoteOutputWriter = zygoteOutputWriter;
+            this.mAbiList = abiList;
+        }
+
+        /**
+         * Create a new ZygoteState object by connecting to the given Zygote socket and saving the
+         * given USAP socket address.
+         *
+         * @param zygoteSocketAddress  Zygote socket to connect to
+         * @param usapSocketAddress  USAP socket address to save for later
+         * @return  A new ZygoteState object containing a session socket for the given Zygote socket
+         * address
+         * @throws IOException
+         */
+        static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
+                @Nullable LocalSocketAddress usapSocketAddress)
+                throws IOException {
+
+            DataInputStream zygoteInputStream;
+            BufferedWriter zygoteOutputWriter;
+            final LocalSocket zygoteSessionSocket = new LocalSocket();
+
+            if (zygoteSocketAddress == null) {
+                throw new IllegalArgumentException("zygoteSocketAddress can't be null");
+            }
+
+            try {
+                zygoteSessionSocket.connect(zygoteSocketAddress);
+                zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
+                zygoteOutputWriter =
+                        new BufferedWriter(
+                                new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
+                                Zygote.SOCKET_BUFFER_SIZE);
+            } catch (IOException ex) {
+                try {
+                    zygoteSessionSocket.close();
+                } catch (IOException ignore) { }
+
+                throw ex;
+            }
+
+            return new ZygoteState(zygoteSocketAddress, usapSocketAddress,
+                                   zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
+                                   getAbiList(zygoteOutputWriter, zygoteInputStream));
+        }
+
+        LocalSocket getUsapSessionSocket() throws IOException {
+            final LocalSocket usapSessionSocket = new LocalSocket();
+            usapSessionSocket.connect(this.mUsapSocketAddress);
+
+            return usapSessionSocket;
+        }
+
+        boolean matches(String abi) {
+            return mAbiList.contains(abi);
+        }
+
+        public void close() {
+            try {
+                mZygoteSessionSocket.close();
+            } catch (IOException ex) {
+                Log.e(LOG_TAG,"I/O exception on routine close", ex);
+            }
+
+            mClosed = true;
+        }
+
+        boolean isClosed() {
+            return mClosed;
+        }
+    }
+
+    /**
+     * Lock object to protect access to the two ZygoteStates below. This lock must be
+     * acquired while communicating over the ZygoteState's socket, to prevent
+     * interleaved access.
+     */
+    private final Object mLock = new Object();
+
+    /**
+     * List of exemptions to the API deny list. These are prefix matches on the runtime format
+     * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
+     * list.
+     */
+    private List<String> mApiDenylistExemptions = Collections.emptyList();
+
+    /**
+     * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000.
+     */
+    private int mHiddenApiAccessLogSampleRate;
+
+    /**
+     * Proportion of hidden API accesses that should be logged to statslog; 0 - 0x10000.
+     */
+    private int mHiddenApiAccessStatslogSampleRate;
+
+    /**
+     * The state of the connection to the primary zygote.
+     */
+    private ZygoteState primaryZygoteState;
+
+    /**
+     * The state of the connection to the secondary zygote.
+     */
+    private ZygoteState secondaryZygoteState;
+
+    /**
+     * If this Zygote supports the creation and maintenance of a USAP pool.
+     *
+     * Currently only the primary and secondary Zygotes support USAP pools. Any
+     * child Zygotes will be unable to create or use a USAP pool.
+     */
+    private final boolean mUsapPoolSupported;
+
+    /**
+     * If the USAP pool should be created and used to start applications.
+     *
+     * Setting this value to false will disable the creation, maintenance, and use of the USAP
+     * pool.  When the USAP pool is disabled the application lifecycle will be identical to
+     * previous versions of Android.
+     */
+    private boolean mUsapPoolEnabled = false;
+
+    /**
+     * Start a new process.
+     *
+     * <p>If processes are enabled, a new process is created and the
+     * static main() function of a <var>processClass</var> is executed there.
+     * The process will continue running after this function returns.
+     *
+     * <p>If processes are not enabled, a new thread in the caller's
+     * process is created and main() of <var>processclass</var> called there.
+     *
+     * <p>The niceName parameter, if not an empty string, is a custom name to
+     * give to the process instead of using processClass.  This allows you to
+     * make easily identifyable processes even if you are using the same base
+     * <var>processClass</var> to start them.
+     *
+     * 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
+     * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
+     *
+     * @param processClass The class to use as the process's main entry
+     *                     point.
+     * @param niceName A more readable name to use for the process.
+     * @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 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.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param appDataDir null-ok the data directory of the app.
+     * @param invokeWith null-ok the command to invoke with.
+     * @param packageName null-ok the name of the package this process belongs to.
+     * @param zygotePolicyFlags Flags used to determine how to launch the application.
+     * @param isTopApp Whether the process starts for high priority application.
+     * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
+     *                             started.
+     * @param pkgDataInfoMap Map from related package names to private data directory
+     *                       volume UUID and inode number.
+     * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
+     * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
+     *
+     * @param zygoteArgs Additional arguments to supply to the Zygote process.
+     * @return An object that describes the result of the attempt to start the process.
+     * @throws RuntimeException on fatal start failure
+     */
+    public final Process.ProcessStartResult start(@NonNull final String processClass,
+                                                  final String niceName,
+                                                  int uid, int gid, @Nullable int[] gids,
+                                                  int runtimeFlags, int mountExternal,
+                                                  int targetSdkVersion,
+                                                  @Nullable String seInfo,
+                                                  @NonNull String abi,
+                                                  @Nullable String instructionSet,
+                                                  @Nullable String appDataDir,
+                                                  @Nullable String invokeWith,
+                                                  @Nullable String packageName,
+                                                  int zygotePolicyFlags,
+                                                  boolean isTopApp,
+                                                  @Nullable long[] disabledCompatChanges,
+                                                  @Nullable Map<String, Pair<String, Long>>
+                                                          pkgDataInfoMap,
+                                                  @Nullable Map<String, Pair<String, Long>>
+                                                          allowlistedDataInfoList,
+                                                  boolean bindMountAppsData,
+                                                  boolean bindMountAppStorageDirs,
+                                                  @Nullable String[] zygoteArgs) {
+        // TODO (chriswailes): Is there a better place to check this value?
+        if (fetchUsapPoolEnabledPropWithMinInterval()) {
+            informZygotesOfUsapPoolStatus();
+        }
+
+        try {
+            return startViaZygote(processClass, niceName, uid, gid, gids,
+                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
+                    abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
+                    packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
+                    pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,
+                    bindMountAppStorageDirs, zygoteArgs);
+        } catch (ZygoteStartFailedEx ex) {
+            Log.e(LOG_TAG,
+                    "Starting VM process through Zygote failed");
+            throw new RuntimeException(
+                    "Starting VM process through Zygote failed", ex);
+        }
+    }
+
+    /** retry interval for opening a zygote socket */
+    static final int ZYGOTE_RETRY_MILLIS = 500;
+
+    /**
+     * Queries the zygote for the list of ABIS it supports.
+     */
+    @GuardedBy("mLock")
+    private static List<String> getAbiList(BufferedWriter writer, DataInputStream inputStream)
+            throws IOException {
+        // Each query starts with the argument count (1 in this case)
+        writer.write("1");
+        // ... followed by a new-line.
+        writer.newLine();
+        // ... followed by our only argument.
+        writer.write("--query-abi-list");
+        writer.newLine();
+        writer.flush();
+
+        // The response is a length prefixed stream of ASCII bytes.
+        int numBytes = inputStream.readInt();
+        byte[] bytes = new byte[numBytes];
+        inputStream.readFully(bytes);
+
+        final String rawList = new String(bytes, StandardCharsets.US_ASCII);
+
+        return Arrays.asList(rawList.split(","));
+    }
+
+    /**
+     * Sends an argument list to the zygote process, which starts a new child
+     * and returns the child's pid. Please note: the present implementation
+     * replaces newlines in the argument list with spaces.
+     *
+     * @throws ZygoteStartFailedEx if process start failed for any reason
+     */
+    @GuardedBy("mLock")
+    private Process.ProcessStartResult zygoteSendArgsAndGetResult(
+            ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)
+            throws ZygoteStartFailedEx {
+        // Throw early if any of the arguments are malformed. This means we can
+        // avoid writing a partial response to the zygote.
+        for (String arg : args) {
+            // Making two indexOf calls here is faster than running a manually fused loop due
+            // to the fact that indexOf is an optimized intrinsic.
+            if (arg.indexOf('\n') >= 0) {
+                throw new ZygoteStartFailedEx("Embedded newlines not allowed");
+            } else if (arg.indexOf('\r') >= 0) {
+                throw new ZygoteStartFailedEx("Embedded carriage returns not allowed");
+            }
+        }
+
+        /*
+         * See com.android.internal.os.ZygoteArguments.parseArgs()
+         * Presently the wire format to the zygote process is:
+         * a) a count of arguments (argc, in essence)
+         * b) a number of newline-separated argument strings equal to count
+         *
+         * After the zygote process reads these it will write the pid of
+         * the child or -1 on failure, followed by boolean to
+         * indicate whether a wrapper process was used.
+         */
+        String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";
+
+        if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {
+            try {
+                return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
+            } catch (IOException ex) {
+                // If there was an IOException using the USAP pool we will log the error and
+                // attempt to start the process through the Zygote.
+                Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - "
+                        + ex.getMessage());
+            }
+        }
+
+        return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
+    }
+
+    private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
+            ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
+        try {
+            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
+            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
+
+            zygoteWriter.write(msgStr);
+            zygoteWriter.flush();
+
+            // Always read the entire result from the input stream to avoid leaving
+            // bytes in the stream for future process starts to accidentally stumble
+            // upon.
+            Process.ProcessStartResult result = new Process.ProcessStartResult();
+            result.pid = zygoteInputStream.readInt();
+            result.usingWrapper = zygoteInputStream.readBoolean();
+
+            if (result.pid < 0) {
+                throw new ZygoteStartFailedEx("fork() failed");
+            }
+
+            return result;
+        } catch (IOException ex) {
+            zygoteState.close();
+            Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
+                    + ex.toString());
+            throw new ZygoteStartFailedEx(ex);
+        }
+    }
+
+    private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
+            ZygoteState zygoteState, String msgStr)
+            throws ZygoteStartFailedEx, IOException {
+        try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
+            final BufferedWriter usapWriter =
+                    new BufferedWriter(
+                            new OutputStreamWriter(usapSessionSocket.getOutputStream()),
+                            Zygote.SOCKET_BUFFER_SIZE);
+            final DataInputStream usapReader =
+                    new DataInputStream(usapSessionSocket.getInputStream());
+
+            usapWriter.write(msgStr);
+            usapWriter.flush();
+
+            Process.ProcessStartResult result = new Process.ProcessStartResult();
+            result.pid = usapReader.readInt();
+            // USAPs can't be used to spawn processes that need wrappers.
+            result.usingWrapper = false;
+
+            if (result.pid >= 0) {
+                return result;
+            } else {
+                throw new ZygoteStartFailedEx("USAP specialization failed");
+            }
+        }
+    }
+
+    /**
+     * Test various member properties and parameters to determine if a launch event should be
+     * handled using an Unspecialized App Process Pool or not.
+     *
+     * @param zygotePolicyFlags Policy flags indicating special behavioral observations about the
+     *                          Zygote command
+     * @param args Arguments that will be passed to the Zygote
+     * @return If the command should be sent to a USAP Pool member or an actual Zygote
+     */
+    private boolean shouldAttemptUsapLaunch(int zygotePolicyFlags, ArrayList<String> args) {
+        return mUsapPoolSupported
+                && mUsapPoolEnabled
+                && policySpecifiesUsapPoolLaunch(zygotePolicyFlags)
+                && commandSupportedByUsap(args);
+    }
+
+    /**
+     * Tests a Zygote policy flag set for various properties that determine if it is eligible for
+     * being handled by an Unspecialized App Process Pool.
+     *
+     * @param zygotePolicyFlags Policy flags indicating special behavioral observations about the
+     *                          Zygote command
+     * @return If the policy allows for use of a USAP pool
+     */
+    private static boolean policySpecifiesUsapPoolLaunch(int zygotePolicyFlags) {
+        /*
+         * Zygote USAP Pool Policy: Launch the new process from the USAP Pool iff the launch event
+         * is latency sensitive but *NOT* a system process.  All system processes are equally
+         * important so we don't want to prioritize one over another.
+         */
+        return (zygotePolicyFlags
+                & (ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS | ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE))
+                == ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+    }
+
+    /**
+     * Flags that may not be passed to a USAP.  These may appear as prefixes to individual Zygote
+     * arguments.
+     */
+    private static final String[] INVALID_USAP_FLAGS = {
+        "--query-abi-list",
+        "--get-pid",
+        "--preload-default",
+        "--preload-package",
+        "--preload-app",
+        "--start-child-zygote",
+        "--set-api-denylist-exemptions",
+        "--hidden-api-log-sampling-rate",
+        "--hidden-api-statslog-sampling-rate",
+        "--invoke-with"
+    };
+
+    /**
+     * Tests a command list to see if it is valid to send to a USAP.
+     *
+     * @param args  Zygote/USAP command arguments
+     * @return  True if the command can be passed to a USAP; false otherwise
+     */
+    private static boolean commandSupportedByUsap(ArrayList<String> args) {
+        for (String flag : args) {
+            for (String badFlag : INVALID_USAP_FLAGS) {
+                if (flag.startsWith(badFlag)) {
+                    return false;
+                }
+            }
+            if (flag.startsWith("--nice-name=")) {
+                // Check if the wrap property is set, usap would ignore it.
+                if (Zygote.getWrapProperty(flag.substring(12)) != null) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Starts a new process via the zygote mechanism.
+     *
+     * @param processClass Class name whose static main() to run
+     * @param niceName 'nice' process name to appear in ps
+     * @param uid a POSIX uid that the new process should setuid() to
+     * @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 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.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param appDataDir null-ok the data directory of the app.
+     * @param startChildZygote Start a sub-zygote. This creates a new zygote process
+     * that has its state cloned from this zygote process.
+     * @param packageName null-ok the name of the package this process belongs to.
+     * @param zygotePolicyFlags Flags used to determine how to launch the application.
+     * @param isTopApp Whether the process starts for high priority application.
+     * @param disabledCompatChanges a list of disabled compat changes for the process being started.
+     * @param pkgDataInfoMap Map from related package names to private data directory volume UUID
+     *                       and inode number.
+     * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
+     * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
+     * @param extraArgs Additional arguments to supply to the zygote process.
+     * @return An object that describes the result of the attempt to start the process.
+     * @throws ZygoteStartFailedEx if process start failed for any reason
+     */
+    private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
+                                                      @Nullable final String niceName,
+                                                      final int uid, final int gid,
+                                                      @Nullable final int[] gids,
+                                                      int runtimeFlags, int mountExternal,
+                                                      int targetSdkVersion,
+                                                      @Nullable String seInfo,
+                                                      @NonNull String abi,
+                                                      @Nullable String instructionSet,
+                                                      @Nullable String appDataDir,
+                                                      @Nullable String invokeWith,
+                                                      boolean startChildZygote,
+                                                      @Nullable String packageName,
+                                                      int zygotePolicyFlags,
+                                                      boolean isTopApp,
+                                                      @Nullable long[] disabledCompatChanges,
+                                                      @Nullable Map<String, Pair<String, Long>>
+                                                              pkgDataInfoMap,
+                                                      @Nullable Map<String, Pair<String, Long>>
+                                                              allowlistedDataInfoList,
+                                                      boolean bindMountAppsData,
+                                                      boolean bindMountAppStorageDirs,
+                                                      @Nullable String[] extraArgs)
+                                                      throws ZygoteStartFailedEx {
+        ArrayList<String> argsForZygote = new ArrayList<>();
+
+        // --runtime-args, --setuid=, --setgid=,
+        // and --setgroups= must go first
+        argsForZygote.add("--runtime-args");
+        argsForZygote.add("--setuid=" + uid);
+        argsForZygote.add("--setgid=" + gid);
+        argsForZygote.add("--runtime-flags=" + runtimeFlags);
+        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+            argsForZygote.add("--mount-external-default");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
+            argsForZygote.add("--mount-external-installer");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
+            argsForZygote.add("--mount-external-pass-through");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+            argsForZygote.add("--mount-external-android-writable");
+        }
+
+        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
+
+        // --setgroups is a comma-separated list
+        if (gids != null && gids.length > 0) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("--setgroups=");
+
+            final int sz = gids.length;
+            for (int i = 0; i < sz; i++) {
+                if (i != 0) {
+                    sb.append(',');
+                }
+                sb.append(gids[i]);
+            }
+
+            argsForZygote.add(sb.toString());
+        }
+
+        if (niceName != null) {
+            argsForZygote.add("--nice-name=" + niceName);
+        }
+
+        if (seInfo != null) {
+            argsForZygote.add("--seinfo=" + seInfo);
+        }
+
+        if (instructionSet != null) {
+            argsForZygote.add("--instruction-set=" + instructionSet);
+        }
+
+        if (appDataDir != null) {
+            argsForZygote.add("--app-data-dir=" + appDataDir);
+        }
+
+        if (invokeWith != null) {
+            argsForZygote.add("--invoke-with");
+            argsForZygote.add(invokeWith);
+        }
+
+        if (startChildZygote) {
+            argsForZygote.add("--start-child-zygote");
+        }
+
+        if (packageName != null) {
+            argsForZygote.add("--package-name=" + packageName);
+        }
+
+        if (isTopApp) {
+            argsForZygote.add(Zygote.START_AS_TOP_APP_ARG);
+        }
+        if (pkgDataInfoMap != null && pkgDataInfoMap.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(Zygote.PKG_DATA_INFO_MAP);
+            sb.append("=");
+            boolean started = false;
+            for (Map.Entry<String, Pair<String, Long>> entry : pkgDataInfoMap.entrySet()) {
+                if (started) {
+                    sb.append(',');
+                }
+                started = true;
+                sb.append(entry.getKey());
+                sb.append(',');
+                sb.append(entry.getValue().first);
+                sb.append(',');
+                sb.append(entry.getValue().second);
+            }
+            argsForZygote.add(sb.toString());
+        }
+        if (allowlistedDataInfoList != null && allowlistedDataInfoList.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(Zygote.ALLOWLISTED_DATA_INFO_MAP);
+            sb.append("=");
+            boolean started = false;
+            for (Map.Entry<String, Pair<String, Long>> entry : allowlistedDataInfoList.entrySet()) {
+                if (started) {
+                    sb.append(',');
+                }
+                started = true;
+                sb.append(entry.getKey());
+                sb.append(',');
+                sb.append(entry.getValue().first);
+                sb.append(',');
+                sb.append(entry.getValue().second);
+            }
+            argsForZygote.add(sb.toString());
+        }
+
+        if (bindMountAppStorageDirs) {
+            argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
+        }
+
+        if (bindMountAppsData) {
+            argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS);
+        }
+
+        if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("--disabled-compat-changes=");
+
+            int sz = disabledCompatChanges.length;
+            for (int i = 0; i < sz; i++) {
+                if (i != 0) {
+                    sb.append(',');
+                }
+                sb.append(disabledCompatChanges[i]);
+            }
+
+            argsForZygote.add(sb.toString());
+        }
+
+        argsForZygote.add(processClass);
+
+        if (extraArgs != null) {
+            Collections.addAll(argsForZygote, extraArgs);
+        }
+
+        synchronized(mLock) {
+            // The USAP pool can not be used if the application will not use the systems graphics
+            // driver.  If that driver is requested use the Zygote application start path.
+            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
+                                              zygotePolicyFlags,
+                                              argsForZygote);
+        }
+    }
+
+    private boolean fetchUsapPoolEnabledProp() {
+        boolean origVal = mUsapPoolEnabled;
+
+        mUsapPoolEnabled = ZygoteConfig.getBool(
+            ZygoteConfig.USAP_POOL_ENABLED, ZygoteConfig.USAP_POOL_ENABLED_DEFAULT);
+
+        boolean valueChanged = origVal != mUsapPoolEnabled;
+
+        if (valueChanged) {
+            Log.i(LOG_TAG, "usapPoolEnabled = " + mUsapPoolEnabled);
+        }
+
+        return valueChanged;
+    }
+
+    private boolean mIsFirstPropCheck = true;
+    private long mLastPropCheckTimestamp = 0;
+
+    private boolean fetchUsapPoolEnabledPropWithMinInterval() {
+        // If this Zygote doesn't support USAPs there is no need to fetch any
+        // properties.
+        if (!mUsapPoolSupported) return false;
+
+        final long currentTimestamp = SystemClock.elapsedRealtime();
+
+        if (mIsFirstPropCheck
+                || (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL)) {
+            mIsFirstPropCheck = false;
+            mLastPropCheckTimestamp = currentTimestamp;
+            return fetchUsapPoolEnabledProp();
+        }
+
+        return false;
+    }
+
+    /**
+     * Closes the connections to the zygote, if they exist.
+     */
+    public void close() {
+        if (primaryZygoteState != null) {
+            primaryZygoteState.close();
+        }
+        if (secondaryZygoteState != null) {
+            secondaryZygoteState.close();
+        }
+    }
+
+    /**
+     * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
+     * and retry if the zygote is unresponsive. This method is a no-op if a connection is
+     * already open.
+     */
+    public void establishZygoteConnectionForAbi(String abi) {
+        try {
+            synchronized(mLock) {
+                openZygoteSocketIfNeeded(abi);
+            }
+        } catch (ZygoteStartFailedEx ex) {
+            throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
+        }
+    }
+
+    /**
+     * Attempt to retrieve the PID of the zygote serving the given abi.
+     */
+    public int getZygotePid(String abi) {
+        try {
+            synchronized (mLock) {
+                ZygoteState state = openZygoteSocketIfNeeded(abi);
+
+                // Each query starts with the argument count (1 in this case)
+                state.mZygoteOutputWriter.write("1");
+                // ... followed by a new-line.
+                state.mZygoteOutputWriter.newLine();
+                // ... followed by our only argument.
+                state.mZygoteOutputWriter.write("--get-pid");
+                state.mZygoteOutputWriter.newLine();
+                state.mZygoteOutputWriter.flush();
+
+                // The response is a length prefixed stream of ASCII bytes.
+                int numBytes = state.mZygoteInputStream.readInt();
+                byte[] bytes = new byte[numBytes];
+                state.mZygoteInputStream.readFully(bytes);
+
+                return Integer.parseInt(new String(bytes, StandardCharsets.US_ASCII));
+            }
+        } catch (Exception ex) {
+            throw new RuntimeException("Failure retrieving pid", ex);
+        }
+    }
+
+    /**
+     * Notify the Zygote processes that boot completed.
+     */
+    public void bootCompleted() {
+        // Notify both the 32-bit and 64-bit zygote.
+        if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+            bootCompleted(Build.SUPPORTED_32_BIT_ABIS[0]);
+        }
+        if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+            bootCompleted(Build.SUPPORTED_64_BIT_ABIS[0]);
+        }
+    }
+
+    private void bootCompleted(String abi) {
+        try {
+            synchronized (mLock) {
+                ZygoteState state = openZygoteSocketIfNeeded(abi);
+                state.mZygoteOutputWriter.write("1\n--boot-completed\n");
+                state.mZygoteOutputWriter.flush();
+                state.mZygoteInputStream.readInt();
+            }
+        } catch (Exception ex) {
+            throw new RuntimeException("Failed to inform zygote of boot_completed", ex);
+        }
+    }
+
+    /**
+     * Push hidden API deny-listing exemptions into the zygote process(es).
+     *
+     * <p>The list of exemptions will take affect for all new processes forked from the zygote after
+     * this call.
+     *
+     * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
+     *        allowed/public APIs (i.e. allowed, no logging of usage).
+     */
+    public boolean setApiDenylistExemptions(List<String> exemptions) {
+        synchronized (mLock) {
+            mApiDenylistExemptions = exemptions;
+            boolean ok = maybeSetApiDenylistExemptions(primaryZygoteState, true);
+            if (ok) {
+                ok = maybeSetApiDenylistExemptions(secondaryZygoteState, true);
+            }
+            return ok;
+        }
+    }
+
+    /**
+     * Set the precentage of detected hidden API accesses that are logged to the event log.
+     *
+     * <p>This rate will take affect for all new processes forked from the zygote after this call.
+     *
+     * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
+     */
+    public void setHiddenApiAccessLogSampleRate(int rate) {
+        synchronized (mLock) {
+            mHiddenApiAccessLogSampleRate = rate;
+            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+        }
+    }
+
+    /**
+     * Set the precentage of detected hidden API accesses that are logged to the new event log.
+     *
+     * <p>This rate will take affect for all new processes forked from the zygote after this call.
+     *
+     * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
+     */
+    public void setHiddenApiAccessStatslogSampleRate(int rate) {
+        synchronized (mLock) {
+            mHiddenApiAccessStatslogSampleRate = rate;
+            maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState);
+            maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean maybeSetApiDenylistExemptions(ZygoteState state, boolean sendIfEmpty) {
+        if (state == null || state.isClosed()) {
+            Slog.e(LOG_TAG, "Can't set API denylist exemptions: no zygote connection");
+            return false;
+        } else if (!sendIfEmpty && mApiDenylistExemptions.isEmpty()) {
+            return true;
+        }
+
+        try {
+            state.mZygoteOutputWriter.write(Integer.toString(mApiDenylistExemptions.size() + 1));
+            state.mZygoteOutputWriter.newLine();
+            state.mZygoteOutputWriter.write("--set-api-denylist-exemptions");
+            state.mZygoteOutputWriter.newLine();
+            for (int i = 0; i < mApiDenylistExemptions.size(); ++i) {
+                state.mZygoteOutputWriter.write(mApiDenylistExemptions.get(i));
+                state.mZygoteOutputWriter.newLine();
+            }
+            state.mZygoteOutputWriter.flush();
+            int status = state.mZygoteInputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set API denylist exemptions; status " + status);
+            }
+            return true;
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set API denylist exemptions", ioe);
+            mApiDenylistExemptions = Collections.emptyList();
+            return false;
+        }
+    }
+
+    private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) {
+        if (state == null || state.isClosed() || mHiddenApiAccessLogSampleRate == -1) {
+            return;
+        }
+
+        try {
+            state.mZygoteOutputWriter.write(Integer.toString(1));
+            state.mZygoteOutputWriter.newLine();
+            state.mZygoteOutputWriter.write("--hidden-api-log-sampling-rate="
+                    + mHiddenApiAccessLogSampleRate);
+            state.mZygoteOutputWriter.newLine();
+            state.mZygoteOutputWriter.flush();
+            int status = state.mZygoteInputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
+            }
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", ioe);
+        }
+    }
+
+    private void maybeSetHiddenApiAccessStatslogSampleRate(ZygoteState state) {
+        if (state == null || state.isClosed() || mHiddenApiAccessStatslogSampleRate == -1) {
+            return;
+        }
+
+        try {
+            state.mZygoteOutputWriter.write(Integer.toString(1));
+            state.mZygoteOutputWriter.newLine();
+            state.mZygoteOutputWriter.write("--hidden-api-statslog-sampling-rate="
+                    + mHiddenApiAccessStatslogSampleRate);
+            state.mZygoteOutputWriter.newLine();
+            state.mZygoteOutputWriter.flush();
+            int status = state.mZygoteInputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set hidden API statslog sampling rate; status "
+                        + status);
+            }
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set hidden API statslog sampling rate", ioe);
+        }
+    }
+
+    /**
+     * Creates a ZygoteState for the primary zygote if it doesn't exist or has been disconnected.
+     */
+    @GuardedBy("mLock")
+    private void attemptConnectionToPrimaryZygote() throws IOException {
+        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
+            primaryZygoteState =
+                    ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);
+
+            maybeSetApiDenylistExemptions(primaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+        }
+    }
+
+    /**
+     * Creates a ZygoteState for the secondary zygote if it doesn't exist or has been disconnected.
+     */
+    @GuardedBy("mLock")
+    private void attemptConnectionToSecondaryZygote() throws IOException {
+        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+            secondaryZygoteState =
+                    ZygoteState.connect(mZygoteSecondarySocketAddress,
+                            mUsapPoolSecondarySocketAddress);
+
+            maybeSetApiDenylistExemptions(secondaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+        }
+    }
+
+    /**
+     * Tries to open a session socket to a Zygote process with a compatible ABI if one is not
+     * already open. If a compatible session socket is already open that session socket is returned.
+     * This function may block and may have to try connecting to multiple Zygotes to find the
+     * appropriate one.  Requires that mLock be held.
+     */
+    @GuardedBy("mLock")
+    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+        try {
+            attemptConnectionToPrimaryZygote();
+
+            if (primaryZygoteState.matches(abi)) {
+                return primaryZygoteState;
+            }
+
+            if (mZygoteSecondarySocketAddress != null) {
+                // The primary zygote didn't match. Try the secondary.
+                attemptConnectionToSecondaryZygote();
+
+                if (secondaryZygoteState.matches(abi)) {
+                    return secondaryZygoteState;
+                }
+            }
+        } catch (IOException ioe) {
+            throw new ZygoteStartFailedEx("Error connecting to zygote", ioe);
+        }
+
+        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
+    }
+
+    /**
+     * Instructs the zygote to pre-load the application code for the given Application.
+     * Only the app zygote supports this function.
+     * TODO preloadPackageForAbi() can probably be removed and the callers an use this instead.
+     */
+    public boolean preloadApp(ApplicationInfo appInfo, String abi)
+            throws ZygoteStartFailedEx, IOException {
+        synchronized (mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            state.mZygoteOutputWriter.write("2");
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.write("--preload-app");
+            state.mZygoteOutputWriter.newLine();
+
+            // Zygote args needs to be strings, so in order to pass ApplicationInfo,
+            // write it to a Parcel, and base64 the raw Parcel bytes to the other side.
+            Parcel parcel = Parcel.obtain();
+            appInfo.writeToParcel(parcel, 0 /* flags */);
+            String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
+            parcel.recycle();
+            state.mZygoteOutputWriter.write(encodedParcelData);
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.flush();
+
+            return (state.mZygoteInputStream.readInt() == 0);
+        }
+    }
+
+    /**
+     * Instructs the zygote to pre-load the classes and native libraries at the given paths
+     * for the specified abi. Not all zygotes support this function.
+     */
+    public boolean preloadPackageForAbi(
+            String packagePath, String libsPath, String libFileName, String cacheKey, String abi)
+            throws ZygoteStartFailedEx, IOException {
+        synchronized (mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            state.mZygoteOutputWriter.write("5");
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.write("--preload-package");
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.write(packagePath);
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.write(libsPath);
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.write(libFileName);
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.write(cacheKey);
+            state.mZygoteOutputWriter.newLine();
+
+            state.mZygoteOutputWriter.flush();
+
+            return (state.mZygoteInputStream.readInt() == 0);
+        }
+    }
+
+    /**
+     * Instructs the zygote to preload the default set of classes and resources. Returns
+     * {@code true} if a preload was performed as a result of this call, and {@code false}
+     * otherwise. The latter usually means that the zygote eagerly preloaded at startup
+     * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous.
+     */
+    public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException {
+        synchronized (mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            // Each query starts with the argument count (1 in this case)
+            state.mZygoteOutputWriter.write("1");
+            state.mZygoteOutputWriter.newLine();
+            state.mZygoteOutputWriter.write("--preload-default");
+            state.mZygoteOutputWriter.newLine();
+            state.mZygoteOutputWriter.flush();
+
+            return (state.mZygoteInputStream.readInt() == 0);
+        }
+    }
+
+    /**
+     * Try connecting to the Zygote over and over again until we hit a time-out.
+     * @param zygoteSocketName The name of the socket to connect to.
+     */
+    public static void waitForConnectionToZygote(String zygoteSocketName) {
+        final LocalSocketAddress zygoteSocketAddress =
+                new LocalSocketAddress(zygoteSocketName, LocalSocketAddress.Namespace.RESERVED);
+        waitForConnectionToZygote(zygoteSocketAddress);
+    }
+
+    /**
+     * Try connecting to the Zygote over and over again until we hit a time-out.
+     * @param zygoteSocketAddress The name of the socket to connect to.
+     */
+    public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) {
+        int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS;
+        for (int n = numRetries; n >= 0; n--) {
+            try {
+                final ZygoteState zs =
+                        ZygoteState.connect(zygoteSocketAddress, null);
+                zs.close();
+                return;
+            } catch (IOException ioe) {
+                Log.w(LOG_TAG,
+                        "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
+            }
+
+            try {
+                Thread.sleep(ZYGOTE_CONNECT_RETRY_DELAY_MS);
+            } catch (InterruptedException ignored) { }
+        }
+        Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket "
+                + zygoteSocketAddress.getName());
+    }
+
+    /**
+     * Sends messages to the zygotes telling them to change the status of their USAP pools.  If
+     * this notification fails the ZygoteProcess will fall back to the previous behavior.
+     */
+    private void informZygotesOfUsapPoolStatus() {
+        final String command = "1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n";
+
+        synchronized (mLock) {
+            try {
+                attemptConnectionToPrimaryZygote();
+
+                primaryZygoteState.mZygoteOutputWriter.write(command);
+                primaryZygoteState.mZygoteOutputWriter.flush();
+            } catch (IOException ioe) {
+                mUsapPoolEnabled = !mUsapPoolEnabled;
+                Log.w(LOG_TAG, "Failed to inform zygotes of USAP pool status: "
+                        + ioe.getMessage());
+                return;
+            }
+
+            if (mZygoteSecondarySocketAddress != null) {
+                try {
+                    attemptConnectionToSecondaryZygote();
+
+                    try {
+                        secondaryZygoteState.mZygoteOutputWriter.write(command);
+                        secondaryZygoteState.mZygoteOutputWriter.flush();
+
+                        // Wait for the secondary Zygote to finish its work.
+                        secondaryZygoteState.mZygoteInputStream.readInt();
+                    } catch (IOException ioe) {
+                        throw new IllegalStateException(
+                                "USAP pool state change cause an irrecoverable error",
+                                ioe);
+                    }
+                } catch (IOException ioe) {
+                    // No secondary zygote present.  This is expected on some devices.
+                }
+            }
+
+            // Wait for the response from the primary zygote here so the primary/secondary zygotes
+            // can work concurrently.
+            try {
+                // Wait for the primary zygote to finish its work.
+                primaryZygoteState.mZygoteInputStream.readInt();
+            } catch (IOException ioe) {
+                throw new IllegalStateException(
+                        "USAP pool state change cause an irrecoverable error",
+                        ioe);
+            }
+        }
+    }
+
+    /**
+     * Starts a new zygote process as a child of this zygote. This is used to create
+     * secondary zygotes that inherit data from the zygote that this object
+     * communicates with. This returns a new ZygoteProcess representing a connection
+     * to the newly created zygote. Throws an exception if the zygote cannot be started.
+     *
+     * @param processClass The class to use as the child zygote's main entry
+     *                     point.
+     * @param niceName A more readable name to use for the process.
+     * @param uid The user-id under which the child zygote will run.
+     * @param gid The group-id under which the child zygote will run.
+     * @param gids Additional group-ids associated with the child zygote process.
+     * @param runtimeFlags Additional flags.
+     * @param seInfo null-ok SELinux information for the child zygote process.
+     * @param abi non-null the ABI of the child zygote
+     * @param acceptedAbiList ABIs this child zygote will accept connections for; this
+     *                        may be different from <code>abi</code> in case the children
+     *                        spawned from this Zygote only communicate using ABI-safe methods.
+     * @param instructionSet null-ok the instruction set to use.
+     * @param uidRangeStart The first UID in the range the child zygote may setuid()/setgid() to
+     * @param uidRangeEnd The last UID in the range the child zygote may setuid()/setgid() to
+     */
+    public ChildZygoteProcess startChildZygote(final String processClass,
+                                               final String niceName,
+                                               int uid, int gid, int[] gids,
+                                               int runtimeFlags,
+                                               String seInfo,
+                                               String abi,
+                                               String acceptedAbiList,
+                                               String instructionSet,
+                                               int uidRangeStart,
+                                               int uidRangeEnd) {
+        // Create an unguessable address in the global abstract namespace.
+        final LocalSocketAddress serverAddress = new LocalSocketAddress(
+                processClass + "/" + UUID.randomUUID().toString());
+
+        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(),
+                                    Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList,
+                                    Zygote.CHILD_ZYGOTE_UID_RANGE_START + uidRangeStart,
+                                    Zygote.CHILD_ZYGOTE_UID_RANGE_END + uidRangeEnd};
+
+        Process.ProcessStartResult result;
+        try {
+            // We will bind mount app data dirs so app zygote can't access /data/data, while
+            // we don't need to bind mount storage dirs as /storage won't be mounted.
+            result = startViaZygote(processClass, niceName, uid, gid,
+                    gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
+                    abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
+                    true /* startChildZygote */, null /* packageName */,
+                    ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
+                    null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+                    null /* allowlistedDataInfoList */, true /* bindMountAppsData*/,
+                    /* bindMountAppStorageDirs */ false, extraArgs);
+
+        } catch (ZygoteStartFailedEx ex) {
+            throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
+        }
+
+        return new ChildZygoteProcess(serverAddress, result.pid);
+    }
+}
diff --git a/android-34/android/os/connectivity/CellularBatteryStats.java b/android-34/android/os/connectivity/CellularBatteryStats.java
new file mode 100644
index 0000000..fc17002
--- /dev/null
+++ b/android-34/android/os/connectivity/CellularBatteryStats.java
@@ -0,0 +1,345 @@
+/*
+ * 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.os.connectivity;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.CellSignalStrength;
+import android.telephony.ModemActivityInfo;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * API for Cellular power stats
+ *
+ * @hide
+ */
+@SystemApi
+public final class CellularBatteryStats implements Parcelable {
+
+    private final long mLoggingDurationMs;
+    private final long mKernelActiveTimeMs;
+    private final long mNumPacketsTx;
+    private final long mNumBytesTx;
+    private final long mNumPacketsRx;
+    private final long mNumBytesRx;
+    private final long mSleepTimeMs;
+    private final long mIdleTimeMs;
+    private final long mRxTimeMs;
+    private final long mEnergyConsumedMaMs;
+    private final long[] mTimeInRatMs;
+    private final long[] mTimeInRxSignalStrengthLevelMs;
+    private final long[] mTxTimeMs;
+    private final long mMonitoredRailChargeConsumedMaMs;
+
+    public static final @NonNull Parcelable.Creator<CellularBatteryStats> CREATOR =
+            new Parcelable.Creator<CellularBatteryStats>() {
+                public CellularBatteryStats createFromParcel(Parcel in) {
+                    long loggingDurationMs = in.readLong();
+                    long kernelActiveTimeMs = in.readLong();
+                    long numPacketsTx = in.readLong();
+                    long numBytesTx = in.readLong();
+                    long numPacketsRx = in.readLong();
+                    long numBytesRx = in.readLong();
+                    long sleepTimeMs = in.readLong();
+                    long idleTimeMs = in.readLong();
+                    long rxTimeMs = in.readLong();
+                    long energyConsumedMaMs = in.readLong();
+                    long[] timeInRatMs = in.createLongArray();
+                    long[] timeInRxSignalStrengthLevelMs = in.createLongArray();
+                    long[] txTimeMs = in.createLongArray();
+                    long monitoredRailChargeConsumedMaMs = in.readLong();
+
+                    return new CellularBatteryStats(loggingDurationMs, kernelActiveTimeMs,
+                            numPacketsTx, numBytesTx, numPacketsRx, numBytesRx, sleepTimeMs,
+                            idleTimeMs, rxTimeMs, energyConsumedMaMs, timeInRatMs,
+                            timeInRxSignalStrengthLevelMs, txTimeMs,
+                            monitoredRailChargeConsumedMaMs);
+                }
+
+                public CellularBatteryStats[] newArray(int size) {
+                    return new CellularBatteryStats[size];
+                }
+            };
+
+    /** @hide **/
+    public CellularBatteryStats(long loggingDurationMs, long kernelActiveTimeMs, long numPacketsTx,
+            long numBytesTx, long numPacketsRx, long numBytesRx, long sleepTimeMs, long idleTimeMs,
+            long rxTimeMs, Long energyConsumedMaMs, long[] timeInRatMs,
+            long[] timeInRxSignalStrengthLevelMs, long[] txTimeMs,
+            long monitoredRailChargeConsumedMaMs) {
+
+        mLoggingDurationMs = loggingDurationMs;
+        mKernelActiveTimeMs = kernelActiveTimeMs;
+        mNumPacketsTx = numPacketsTx;
+        mNumBytesTx = numBytesTx;
+        mNumPacketsRx = numPacketsRx;
+        mNumBytesRx = numBytesRx;
+        mSleepTimeMs = sleepTimeMs;
+        mIdleTimeMs = idleTimeMs;
+        mRxTimeMs = rxTimeMs;
+        mEnergyConsumedMaMs = energyConsumedMaMs;
+        mTimeInRatMs = Arrays.copyOfRange(
+                timeInRatMs, 0,
+                Math.min(timeInRatMs.length, BatteryStats.NUM_DATA_CONNECTION_TYPES));
+        mTimeInRxSignalStrengthLevelMs = Arrays.copyOfRange(
+                timeInRxSignalStrengthLevelMs, 0,
+                Math.min(timeInRxSignalStrengthLevelMs.length,
+                        CellSignalStrength.getNumSignalStrengthLevels()));
+        mTxTimeMs = Arrays.copyOfRange(
+                txTimeMs, 0,
+                Math.min(txTimeMs.length, ModemActivityInfo.getNumTxPowerLevels()));
+        mMonitoredRailChargeConsumedMaMs = monitoredRailChargeConsumedMaMs;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeLong(mLoggingDurationMs);
+        out.writeLong(mKernelActiveTimeMs);
+        out.writeLong(mNumPacketsTx);
+        out.writeLong(mNumBytesTx);
+        out.writeLong(mNumPacketsRx);
+        out.writeLong(mNumBytesRx);
+        out.writeLong(mSleepTimeMs);
+        out.writeLong(mIdleTimeMs);
+        out.writeLong(mRxTimeMs);
+        out.writeLong(mEnergyConsumedMaMs);
+        out.writeLongArray(mTimeInRatMs);
+        out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
+        out.writeLongArray(mTxTimeMs);
+        out.writeLong(mMonitoredRailChargeConsumedMaMs);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (!(other instanceof CellularBatteryStats)) return false;
+        if (other == this) return true;
+        CellularBatteryStats otherStats = (CellularBatteryStats) other;
+        return this.mLoggingDurationMs == otherStats.mLoggingDurationMs
+                && this.mKernelActiveTimeMs == otherStats.mKernelActiveTimeMs
+                && this.mNumPacketsTx == otherStats.mNumPacketsTx
+                && this.mNumBytesTx == otherStats.mNumBytesTx
+                && this.mNumPacketsRx == otherStats.mNumPacketsRx
+                && this.mNumBytesRx == otherStats.mNumBytesRx
+                && this.mSleepTimeMs == otherStats.mSleepTimeMs
+                && this.mIdleTimeMs == otherStats.mIdleTimeMs
+                && this.mRxTimeMs == otherStats.mRxTimeMs
+                && this.mEnergyConsumedMaMs == otherStats.mEnergyConsumedMaMs
+                && Arrays.equals(this.mTimeInRatMs, otherStats.mTimeInRatMs)
+                && Arrays.equals(this.mTimeInRxSignalStrengthLevelMs,
+                otherStats.mTimeInRxSignalStrengthLevelMs)
+                && Arrays.equals(this.mTxTimeMs, otherStats.mTxTimeMs)
+                && this.mMonitoredRailChargeConsumedMaMs
+                == otherStats.mMonitoredRailChargeConsumedMaMs;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLoggingDurationMs, mKernelActiveTimeMs, mNumPacketsTx,
+                mNumBytesTx, mNumPacketsRx, mNumBytesRx, mSleepTimeMs, mIdleTimeMs,
+                mRxTimeMs, mEnergyConsumedMaMs, Arrays.hashCode(mTimeInRatMs),
+                Arrays.hashCode(mTimeInRxSignalStrengthLevelMs), Arrays.hashCode(mTxTimeMs),
+                mMonitoredRailChargeConsumedMaMs);
+    }
+
+    /**
+     * Returns the duration for which these cellular stats were collected.
+     *
+     * @return Duration of stats collection in milliseconds.
+     */
+    public long getLoggingDurationMillis() {
+        return mLoggingDurationMs;
+    }
+
+    /**
+     * Returns the duration for which the kernel was active within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of kernel active time in milliseconds.
+     */
+    public long getKernelActiveTimeMillis() {
+        return mKernelActiveTimeMs;
+    }
+
+    /**
+     * Returns the number of packets transmitted over cellular within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of packets transmitted.
+     */
+    public long getNumPacketsTx() {
+        return mNumPacketsTx;
+    }
+
+    /**
+     * Returns the number of packets received over cellular within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of packets received.
+     */
+    public long getNumBytesTx() {
+        return mNumBytesTx;
+    }
+
+    /**
+     * Returns the number of bytes transmitted over cellular within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of bytes transmitted.
+     */
+    public long getNumPacketsRx() {
+        return mNumPacketsRx;
+    }
+
+    /**
+     * Returns the number of bytes received over cellular within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of bytes received.
+     */
+    public long getNumBytesRx() {
+        return mNumBytesRx;
+    }
+
+    /**
+     * Returns the duration for which the device was sleeping within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of sleep time in milliseconds.
+     */
+    public long getSleepTimeMillis() {
+        return mSleepTimeMs;
+    }
+
+    /**
+     * Returns the duration for which the device was idle within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of idle time in milliseconds.
+     */
+    public long getIdleTimeMillis() {
+        return mIdleTimeMs;
+    }
+
+    /**
+     * Returns the duration for which the device was receiving over cellular within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of cellular reception time in milliseconds.
+     */
+    public long getRxTimeMillis() {
+        return mRxTimeMs;
+    }
+
+    /**
+     * Returns an estimation of energy consumed by cellular chip within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Energy consumed in milli-ampere milliseconds (mAmS).
+     */
+    public long getEnergyConsumedMaMillis() {
+        return mEnergyConsumedMaMs;
+    }
+
+    /**
+     * Returns the time in microseconds that the phone has been running with
+     * the given data connection.
+     *
+     * @param networkType The network type to query.
+     * @return The amount of time the phone spends in the {@code networkType} network type. The
+     * unit is in microseconds.
+     */
+    @NonNull
+    @SuppressLint("MethodNameUnits")
+    public long getTimeInRatMicros(@NetworkType int networkType) {
+        if (networkType >= mTimeInRatMs.length) {
+            return -1;
+        }
+
+        return mTimeInRatMs[networkType];
+    }
+
+    /**
+     * Returns the time in microseconds that the phone has been running with
+     * the given signal strength.
+     *
+     * @param signalStrengthBin a single integer from 0 to 4 representing the general signal
+     * quality.
+     * @return Amount of time phone spends in specific cellular rx signal strength levels
+     * in microseconds. The index is signal strength bin.
+     */
+    @NonNull
+    @SuppressLint("MethodNameUnits")
+    public long getTimeInRxSignalStrengthLevelMicros(
+            @IntRange(from = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                    to = CellSignalStrength.SIGNAL_STRENGTH_GREAT) int signalStrengthBin) {
+        if (signalStrengthBin >= mTimeInRxSignalStrengthLevelMs.length) {
+            return -1;
+        }
+        return mTimeInRxSignalStrengthLevelMs[signalStrengthBin];
+    }
+
+    /**
+     * Returns the duration for which the device was transmitting over cellular within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @param level a single integer from 0 to 4 representing the Tx(transmit) power level.
+     * @return Duration of cellular transmission time for specific power level in milliseconds.
+     *
+     * Tx(transmit) power level. see power index @ModemActivityInfo.TxPowerLevel below
+     * <ul>
+     * <li> index 0 = tx_power < 0dBm. </li>
+     * <li> index 1 = 0dBm < tx_power < 5dBm. </li>
+     * <li> index 2 = 5dBm < tx_power < 15dBm. </li>
+     * <li> index 3 = 15dBm < tx_power < 20dBm. </li>
+     * <li> index 4 = tx_power > 20dBm. </li>
+     * </ul>
+     *
+     * @hide
+     */
+    @NonNull
+    public long getTxTimeMillis(
+            @IntRange(from = ModemActivityInfo.TX_POWER_LEVEL_0,
+                    to = ModemActivityInfo.TX_POWER_LEVEL_4) int level) {
+        if (level >= mTxTimeMs.length) {
+            return -1;
+        }
+
+        return mTxTimeMs[level];
+    }
+
+    /**
+     * Returns the energy consumed by cellular chip within {@link #getLoggingDurationMillis()}.
+     *
+     * @return Energy consumed in milli-ampere milli-seconds (mAmS).
+     */
+    public long getMonitoredRailChargeConsumedMaMillis() {
+        return mMonitoredRailChargeConsumedMaMs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-34/android/os/connectivity/GpsBatteryStats.java b/android-34/android/os/connectivity/GpsBatteryStats.java
new file mode 100644
index 0000000..5e21bd3
--- /dev/null
+++ b/android-34/android/os/connectivity/GpsBatteryStats.java
@@ -0,0 +1,107 @@
+/*
+ * 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.os.connectivity;
+
+import android.location.GnssSignalQuality;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * API for GPS power stats
+ *
+ * @hide
+ */
+public final class GpsBatteryStats implements Parcelable {
+
+  private long mLoggingDurationMs;
+  private long mEnergyConsumedMaMs;
+  private long[] mTimeInGpsSignalQualityLevel;
+
+  public static final @android.annotation.NonNull Parcelable.Creator<GpsBatteryStats> CREATOR = new
+      Parcelable.Creator<GpsBatteryStats>() {
+        public GpsBatteryStats createFromParcel(Parcel in) {
+          return new GpsBatteryStats(in);
+        }
+
+        public GpsBatteryStats[] newArray(int size) {
+          return new GpsBatteryStats[size];
+        }
+      };
+
+  public GpsBatteryStats() {
+    initialize();
+  }
+
+  @Override
+  public void writeToParcel(Parcel out, int flags) {
+    out.writeLong(mLoggingDurationMs);
+    out.writeLong(mEnergyConsumedMaMs);
+    out.writeLongArray(mTimeInGpsSignalQualityLevel);
+  }
+
+  public void readFromParcel(Parcel in) {
+    mLoggingDurationMs = in.readLong();
+    mEnergyConsumedMaMs = in.readLong();
+    in.readLongArray(mTimeInGpsSignalQualityLevel);
+  }
+
+  public long getLoggingDurationMs() {
+    return mLoggingDurationMs;
+  }
+
+  public long getEnergyConsumedMaMs() {
+    return mEnergyConsumedMaMs;
+  }
+
+  public long[] getTimeInGpsSignalQualityLevel() {
+    return mTimeInGpsSignalQualityLevel;
+  }
+
+  public void setLoggingDurationMs(long t) {
+    mLoggingDurationMs = t;
+    return;
+  }
+
+  public void setEnergyConsumedMaMs(long e) {
+    mEnergyConsumedMaMs = e;
+    return;
+  }
+
+  public void setTimeInGpsSignalQualityLevel(long[] t) {
+    mTimeInGpsSignalQualityLevel = Arrays.copyOfRange(t, 0,
+        Math.min(t.length, GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS));
+    return;
+  }
+
+  @Override
+  public int describeContents() {
+    return 0;
+  }
+
+  private GpsBatteryStats(Parcel in) {
+    initialize();
+    readFromParcel(in);
+  }
+
+  private void initialize() {
+    mLoggingDurationMs = 0;
+    mEnergyConsumedMaMs = 0;
+    mTimeInGpsSignalQualityLevel = new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
+    return;
+  }
+}
\ No newline at end of file
diff --git a/android-34/android/os/connectivity/WifiActivityEnergyInfo.java b/android-34/android/os/connectivity/WifiActivityEnergyInfo.java
new file mode 100644
index 0000000..ad74a9f
--- /dev/null
+++ b/android-34/android/os/connectivity/WifiActivityEnergyInfo.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 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.os.connectivity;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.os.PowerProfile;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Record of energy and activity information from controller and
+ * underlying wifi stack state. Timestamp the record with elapsed
+ * real-time.
+ * @hide
+ */
+@SystemApi
+public final class WifiActivityEnergyInfo implements Parcelable {
+    @ElapsedRealtimeLong
+    private final long mTimeSinceBootMillis;
+    @StackState
+    private final int mStackState;
+    @IntRange(from = 0)
+    private final long mControllerTxDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerRxDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerScanDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerIdleDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerEnergyUsedMicroJoules;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"STACK_STATE_"}, value = {
+            STACK_STATE_INVALID,
+            STACK_STATE_STATE_ACTIVE,
+            STACK_STATE_STATE_SCANNING,
+            STACK_STATE_STATE_IDLE})
+    public @interface StackState {}
+
+    /** Invalid Wifi stack state. */
+    public static final int STACK_STATE_INVALID = 0;
+    /** Wifi stack is active. */
+    public static final int STACK_STATE_STATE_ACTIVE = 1;
+    /** Wifi stack is scanning. */
+    public static final int STACK_STATE_STATE_SCANNING = 2;
+    /** Wifi stack is idle. */
+    public static final int STACK_STATE_STATE_IDLE = 3;
+
+    /**
+     * Constructor.
+     *
+     * @param timeSinceBootMillis the elapsed real time since boot, in milliseconds.
+     * @param stackState The current state of the Wifi Stack. One of {@link #STACK_STATE_INVALID},
+     *                   {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+     *                   or {@link #STACK_STATE_STATE_IDLE}.
+     * @param txDurationMillis Cumulative milliseconds of active transmission.
+     * @param rxDurationMillis Cumulative milliseconds of active receive.
+     * @param scanDurationMillis Cumulative milliseconds when radio is awake due to scan.
+     * @param idleDurationMillis Cumulative milliseconds when radio is awake but not transmitting or
+     *                       receiving.
+     */
+    public WifiActivityEnergyInfo(
+            @ElapsedRealtimeLong long timeSinceBootMillis,
+            @StackState int stackState,
+            @IntRange(from = 0) long txDurationMillis,
+            @IntRange(from = 0) long rxDurationMillis,
+            @IntRange(from = 0) long scanDurationMillis,
+            @IntRange(from = 0) long idleDurationMillis) {
+
+        this(timeSinceBootMillis,
+                stackState,
+                txDurationMillis,
+                rxDurationMillis,
+                scanDurationMillis,
+                idleDurationMillis,
+                calculateEnergyMicroJoules(txDurationMillis, rxDurationMillis, idleDurationMillis));
+    }
+
+    private static long calculateEnergyMicroJoules(
+            long txDurationMillis, long rxDurationMillis, long idleDurationMillis) {
+        final Context context = ActivityThread.currentActivityThread().getSystemContext();
+        if (context == null) {
+            return 0L;
+        }
+        // Calculate energy used using PowerProfile.
+        PowerProfile powerProfile = new PowerProfile(context);
+        final double idleCurrent = powerProfile.getAveragePower(
+                PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
+        final double rxCurrent = powerProfile.getAveragePower(
+                PowerProfile.POWER_WIFI_CONTROLLER_RX);
+        final double txCurrent = powerProfile.getAveragePower(
+                PowerProfile.POWER_WIFI_CONTROLLER_TX);
+        final double voltage = powerProfile.getAveragePower(
+                PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+
+        return (long) ((txDurationMillis * txCurrent
+                + rxDurationMillis * rxCurrent
+                + idleDurationMillis * idleCurrent)
+                * voltage);
+    }
+
+    /** @hide */
+    public WifiActivityEnergyInfo(
+            @ElapsedRealtimeLong long timeSinceBootMillis,
+            @StackState int stackState,
+            @IntRange(from = 0) long txDurationMillis,
+            @IntRange(from = 0) long rxDurationMillis,
+            @IntRange(from = 0) long scanDurationMillis,
+            @IntRange(from = 0) long idleDurationMillis,
+            @IntRange(from = 0) long energyUsedMicroJoules) {
+        mTimeSinceBootMillis = timeSinceBootMillis;
+        mStackState = stackState;
+        mControllerTxDurationMillis = txDurationMillis;
+        mControllerRxDurationMillis = rxDurationMillis;
+        mControllerScanDurationMillis = scanDurationMillis;
+        mControllerIdleDurationMillis = idleDurationMillis;
+        mControllerEnergyUsedMicroJoules = energyUsedMicroJoules;
+    }
+
+    @Override
+    public String toString() {
+        return "WifiActivityEnergyInfo{"
+                + " mTimeSinceBootMillis=" + mTimeSinceBootMillis
+                + " mStackState=" + mStackState
+                + " mControllerTxDurationMillis=" + mControllerTxDurationMillis
+                + " mControllerRxDurationMillis=" + mControllerRxDurationMillis
+                + " mControllerScanDurationMillis=" + mControllerScanDurationMillis
+                + " mControllerIdleDurationMillis=" + mControllerIdleDurationMillis
+                + " mControllerEnergyUsedMicroJoules=" + mControllerEnergyUsedMicroJoules
+                + " }";
+    }
+
+    public static final @NonNull Parcelable.Creator<WifiActivityEnergyInfo> CREATOR =
+            new Parcelable.Creator<WifiActivityEnergyInfo>() {
+        public WifiActivityEnergyInfo createFromParcel(Parcel in) {
+            long timestamp = in.readLong();
+            int stackState = in.readInt();
+            long txTime = in.readLong();
+            long rxTime = in.readLong();
+            long scanTime = in.readLong();
+            long idleTime = in.readLong();
+            return new WifiActivityEnergyInfo(timestamp, stackState,
+                    txTime, rxTime, scanTime, idleTime);
+        }
+        public WifiActivityEnergyInfo[] newArray(int size) {
+            return new WifiActivityEnergyInfo[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeLong(mTimeSinceBootMillis);
+        out.writeInt(mStackState);
+        out.writeLong(mControllerTxDurationMillis);
+        out.writeLong(mControllerRxDurationMillis);
+        out.writeLong(mControllerScanDurationMillis);
+        out.writeLong(mControllerIdleDurationMillis);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Get the timestamp (elapsed real time milliseconds since boot) of record creation. */
+    @ElapsedRealtimeLong
+    public long getTimeSinceBootMillis() {
+        return mTimeSinceBootMillis;
+    }
+
+    /**
+     * Get the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
+     * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+     * {@link #STACK_STATE_STATE_IDLE}.
+     */
+    @StackState
+    public int getStackState() {
+        return mStackState;
+    }
+
+    /** Get the Wifi transmission duration, in milliseconds. */
+    @IntRange(from = 0)
+    public long getControllerTxDurationMillis() {
+        return mControllerTxDurationMillis;
+    }
+
+    /** Get the Wifi receive duration, in milliseconds. */
+    @IntRange(from = 0)
+    public long getControllerRxDurationMillis() {
+        return mControllerRxDurationMillis;
+    }
+
+    /** Get the Wifi scan duration, in milliseconds. */
+    @IntRange(from = 0)
+    public long getControllerScanDurationMillis() {
+        return mControllerScanDurationMillis;
+    }
+
+    /** Get the Wifi idle duration, in milliseconds. */
+    @IntRange(from = 0)
+    public long getControllerIdleDurationMillis() {
+        return mControllerIdleDurationMillis;
+    }
+
+    /** Get the energy consumed by Wifi, in microjoules. */
+    @IntRange(from = 0)
+    public long getControllerEnergyUsedMicroJoules() {
+        return mControllerEnergyUsedMicroJoules;
+    }
+
+    /**
+     * Returns true if the record is valid, false otherwise.
+     * @hide
+     */
+    public boolean isValid() {
+        return mControllerTxDurationMillis >= 0
+                && mControllerRxDurationMillis >= 0
+                && mControllerScanDurationMillis >= 0
+                && mControllerIdleDurationMillis >= 0;
+    }
+}
diff --git a/android-34/android/os/connectivity/WifiBatteryStats.java b/android-34/android/os/connectivity/WifiBatteryStats.java
new file mode 100644
index 0000000..7e6ebcf
--- /dev/null
+++ b/android-34/android/os/connectivity/WifiBatteryStats.java
@@ -0,0 +1,321 @@
+/*
+ * 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.os.connectivity;
+
+import static android.os.BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS;
+import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
+import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Class for holding Wifi related battery stats
+ *
+ * @hide
+ */
+@SystemApi
+public final class WifiBatteryStats implements Parcelable {
+    private final long mLoggingDurationMillis;
+    private final long mKernelActiveTimeMillis;
+    private final long mNumPacketsTx;
+    private final long mNumBytesTx;
+    private final long mNumPacketsRx;
+    private final long mNumBytesRx;
+    private final long mSleepTimeMillis;
+    private final long mScanTimeMillis;
+    private final long mIdleTimeMillis;
+    private final long mRxTimeMillis;
+    private final long mTxTimeMillis;
+    private final long mEnergyConsumedMaMillis;
+    private final long mAppScanRequestCount;
+    private final long[] mTimeInStateMillis;
+    private final long[] mTimeInSupplicantStateMillis;
+    private final long[] mTimeInRxSignalStrengthLevelMillis;
+    private final long mMonitoredRailChargeConsumedMaMillis;
+
+    public static final @NonNull Parcelable.Creator<WifiBatteryStats> CREATOR =
+            new Parcelable.Creator<WifiBatteryStats>() {
+                public WifiBatteryStats createFromParcel(Parcel in) {
+                    long loggingDurationMillis = in.readLong();
+                    long kernelActiveTimeMillis = in.readLong();
+                    long numPacketsTx = in.readLong();
+                    long numBytesTx = in.readLong();
+                    long numPacketsRx = in.readLong();
+                    long numBytesRx = in.readLong();
+                    long sleepTimeMillis = in.readLong();
+                    long scanTimeMillis = in.readLong();
+                    long idleTimeMillis = in.readLong();
+                    long rxTimeMillis = in.readLong();
+                    long txTimeMillis = in.readLong();
+                    long energyConsumedMaMillis = in.readLong();
+                    long appScanRequestCount = in.readLong();
+                    long[] timeInStateMillis = in.createLongArray();
+                    long[] timeInRxSignalStrengthLevelMillis = in.createLongArray();
+                    long[] timeInSupplicantStateMillis = in.createLongArray();
+                    long monitoredRailChargeConsumedMaMillis = in.readLong();
+                    return new WifiBatteryStats(loggingDurationMillis, kernelActiveTimeMillis,
+                            numPacketsTx, numBytesTx, numPacketsRx, numBytesRx, sleepTimeMillis,
+                            scanTimeMillis, idleTimeMillis, rxTimeMillis, txTimeMillis,
+                            energyConsumedMaMillis, appScanRequestCount, timeInStateMillis,
+                            timeInRxSignalStrengthLevelMillis, timeInSupplicantStateMillis,
+                            monitoredRailChargeConsumedMaMillis);
+                }
+
+                public WifiBatteryStats[] newArray(int size) {
+                    return new WifiBatteryStats[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeLong(mLoggingDurationMillis);
+        out.writeLong(mKernelActiveTimeMillis);
+        out.writeLong(mNumPacketsTx);
+        out.writeLong(mNumBytesTx);
+        out.writeLong(mNumPacketsRx);
+        out.writeLong(mNumBytesRx);
+        out.writeLong(mSleepTimeMillis);
+        out.writeLong(mScanTimeMillis);
+        out.writeLong(mIdleTimeMillis);
+        out.writeLong(mRxTimeMillis);
+        out.writeLong(mTxTimeMillis);
+        out.writeLong(mEnergyConsumedMaMillis);
+        out.writeLong(mAppScanRequestCount);
+        out.writeLongArray(mTimeInStateMillis);
+        out.writeLongArray(mTimeInRxSignalStrengthLevelMillis);
+        out.writeLongArray(mTimeInSupplicantStateMillis);
+        out.writeLong(mMonitoredRailChargeConsumedMaMillis);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (!(other instanceof WifiBatteryStats)) return false;
+        if (other == this) return true;
+        WifiBatteryStats otherStats = (WifiBatteryStats) other;
+        return this.mLoggingDurationMillis == otherStats.mLoggingDurationMillis
+                && this.mKernelActiveTimeMillis == otherStats.mKernelActiveTimeMillis
+                && this.mNumPacketsTx == otherStats.mNumPacketsTx
+                && this.mNumBytesTx == otherStats.mNumBytesTx
+                && this.mNumPacketsRx == otherStats.mNumPacketsRx
+                && this.mNumBytesRx == otherStats.mNumBytesRx
+                && this.mSleepTimeMillis == otherStats.mSleepTimeMillis
+                && this.mScanTimeMillis == otherStats.mScanTimeMillis
+                && this.mIdleTimeMillis == otherStats.mIdleTimeMillis
+                && this.mRxTimeMillis == otherStats.mRxTimeMillis
+                && this.mTxTimeMillis == otherStats.mTxTimeMillis
+                && this.mEnergyConsumedMaMillis == otherStats.mEnergyConsumedMaMillis
+                && this.mAppScanRequestCount == otherStats.mAppScanRequestCount
+                && Arrays.equals(this.mTimeInStateMillis, otherStats.mTimeInStateMillis)
+                && Arrays.equals(this.mTimeInSupplicantStateMillis,
+                    otherStats.mTimeInSupplicantStateMillis)
+                && Arrays.equals(this.mTimeInRxSignalStrengthLevelMillis,
+                    otherStats.mTimeInRxSignalStrengthLevelMillis)
+                && this.mMonitoredRailChargeConsumedMaMillis
+                    == otherStats.mMonitoredRailChargeConsumedMaMillis;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLoggingDurationMillis, mKernelActiveTimeMillis, mNumPacketsTx,
+                mNumBytesTx, mNumPacketsRx, mNumBytesRx, mSleepTimeMillis, mScanTimeMillis,
+                mIdleTimeMillis, mRxTimeMillis, mTxTimeMillis, mEnergyConsumedMaMillis,
+                mAppScanRequestCount, Arrays.hashCode(mTimeInStateMillis),
+                Arrays.hashCode(mTimeInSupplicantStateMillis),
+                Arrays.hashCode(mTimeInRxSignalStrengthLevelMillis),
+                mMonitoredRailChargeConsumedMaMillis);
+    }
+
+    /** @hide **/
+    public WifiBatteryStats(long loggingDurationMillis, long kernelActiveTimeMillis,
+            long numPacketsTx, long numBytesTx, long numPacketsRx, long numBytesRx,
+            long sleepTimeMillis, long scanTimeMillis, long idleTimeMillis, long rxTimeMillis,
+            long txTimeMillis, long energyConsumedMaMillis, long appScanRequestCount,
+            @NonNull long[] timeInStateMillis, @NonNull long [] timeInRxSignalStrengthLevelMillis,
+            @NonNull long[] timeInSupplicantStateMillis, long monitoredRailChargeConsumedMaMillis) {
+        mLoggingDurationMillis = loggingDurationMillis;
+        mKernelActiveTimeMillis = kernelActiveTimeMillis;
+        mNumPacketsTx = numPacketsTx;
+        mNumBytesTx = numBytesTx;
+        mNumPacketsRx = numPacketsRx;
+        mNumBytesRx = numBytesRx;
+        mSleepTimeMillis = sleepTimeMillis;
+        mScanTimeMillis = scanTimeMillis;
+        mIdleTimeMillis = idleTimeMillis;
+        mRxTimeMillis = rxTimeMillis;
+        mTxTimeMillis = txTimeMillis;
+        mEnergyConsumedMaMillis = energyConsumedMaMillis;
+        mAppScanRequestCount = appScanRequestCount;
+        mTimeInStateMillis = Arrays.copyOfRange(
+                timeInStateMillis, 0,
+                Math.min(timeInStateMillis.length, NUM_WIFI_STATES));
+        mTimeInRxSignalStrengthLevelMillis = Arrays.copyOfRange(
+                timeInRxSignalStrengthLevelMillis, 0,
+                Math.min(timeInRxSignalStrengthLevelMillis.length, NUM_WIFI_SIGNAL_STRENGTH_BINS));
+        mTimeInSupplicantStateMillis = Arrays.copyOfRange(
+                timeInSupplicantStateMillis, 0,
+                Math.min(timeInSupplicantStateMillis.length, NUM_WIFI_SUPPL_STATES));
+        mMonitoredRailChargeConsumedMaMillis = monitoredRailChargeConsumedMaMillis;
+    }
+
+    /**
+     * Returns the duration for which these wifi stats were collected.
+     *
+     * @return Duration of stats collection in millis.
+     */
+    public long getLoggingDurationMillis() {
+        return mLoggingDurationMillis;
+    }
+
+    /**
+     * Returns the duration for which the kernel was active within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of kernel active time in millis.
+     */
+    public long getKernelActiveTimeMillis() {
+        return mKernelActiveTimeMillis;
+    }
+
+    /**
+     * Returns the number of packets transmitted over wifi within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of packets transmitted.
+     */
+    public long getNumPacketsTx() {
+        return mNumPacketsTx;
+    }
+
+    /**
+     * Returns the number of bytes transmitted over wifi within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of bytes transmitted.
+     */
+    public long getNumBytesTx() {
+        return mNumBytesTx;
+    }
+
+    /**
+     * Returns the number of packets received over wifi within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of packets received.
+     */
+    public long getNumPacketsRx() {
+        return mNumPacketsRx;
+    }
+
+    /**
+     * Returns the number of bytes received over wifi within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of bytes received.
+     */
+    public long getNumBytesRx() {
+        return mNumBytesRx;
+    }
+
+    /**
+     * Returns the duration for which the device was sleeping within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of sleep time in millis.
+     */
+    public long getSleepTimeMillis() {
+        return mSleepTimeMillis;
+    }
+
+    /**
+     * Returns the duration for which the device was wifi scanning within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of wifi scanning time in millis.
+     */
+    public long getScanTimeMillis() {
+        return mScanTimeMillis;
+    }
+
+    /**
+     * Returns the duration for which the device was idle within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of idle time in millis.
+     */
+    public long getIdleTimeMillis() {
+        return mIdleTimeMillis;
+    }
+
+    /**
+     * Returns the duration for which the device was receiving over wifi within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of wifi reception time in millis.
+     */
+    public long getRxTimeMillis() {
+        return mRxTimeMillis;
+    }
+
+    /**
+     * Returns the duration for which the device was transmitting over wifi within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Duration of wifi transmission time in millis.
+     */
+    public long getTxTimeMillis() {
+        return mTxTimeMillis;
+    }
+
+    /**
+     * Returns an estimation of energy consumed in millis by wifi chip within
+     * {@link #getLoggingDurationMillis()}.
+     *
+     * @return Energy consumed in millis.
+     */
+    public long getEnergyConsumedMaMillis() {
+        return mEnergyConsumedMaMillis;
+    }
+
+    /**
+     * Returns the number of app initiated wifi scans within {@link #getLoggingDurationMillis()}.
+     *
+     * @return Number of app scans.
+     */
+    public long getAppScanRequestCount() {
+        return mAppScanRequestCount;
+    }
+
+    /**
+     * Returns the energy consumed by wifi chip within {@link #getLoggingDurationMillis()}.
+     *
+     * @return Energy consumed in millis.
+     */
+    public long getMonitoredRailChargeConsumedMaMillis() {
+        return mMonitoredRailChargeConsumedMaMillis;
+    }
+}
diff --git a/android-34/android/os/health/HealthKeys.java b/android-34/android/os/health/HealthKeys.java
new file mode 100644
index 0000000..5d60411
--- /dev/null
+++ b/android-34/android/os/health/HealthKeys.java
@@ -0,0 +1,219 @@
+/*
+ * 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.os.health;
+
+import android.annotation.TestApi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+/**
+ * Constants and stuff for the android.os.health package.
+ *
+ * @hide
+ */
+@TestApi
+public class HealthKeys {
+
+    /**
+     * No valid key will ever be 0.
+     */
+    public static final int UNKNOWN_KEY = 0;
+
+    /*
+     * Base key for each of the different classes. There is
+     * nothing intrinsic to the operation of the value of the
+     * keys. It's just segmented for better debugging. The
+     * classes don't mix them anway.
+     */
+    public static final int BASE_UID = 10000;
+    public static final int BASE_PID = 20000;
+    public static final int BASE_PROCESS = 30000;
+    public static final int BASE_PACKAGE = 40000;
+    public static final int BASE_SERVICE = 50000;
+
+    /*
+     * The types of values supported by HealthStats.
+     */
+    public static final int TYPE_TIMER = 0;
+    public static final int TYPE_MEASUREMENT = 1;
+    public static final int TYPE_STATS = 2;
+    public static final int TYPE_TIMERS = 3;
+    public static final int TYPE_MEASUREMENTS = 4;
+
+    public static final int TYPE_COUNT = 5;
+
+    /**
+     * Annotation to mark public static final int fields that are to be used
+     * as field keys in HealthStats.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD})
+    public @interface Constant {
+        /**
+         * One of the TYPE_* constants above.
+         */
+        int type();
+    }
+
+    /**
+     * Class to gather the constants defined in a class full of constants and
+     * build the key indices used by HealthStatsWriter and HealthStats.
+     *
+     * @hide
+     */
+    @TestApi
+    public static class Constants {
+        private final String mDataType;
+        private final int[][] mKeys = new int[TYPE_COUNT][];
+
+        /**
+         * Pass in a class to gather the public static final int fields that are
+         * tagged with the @Constant annotation.
+         */
+        public Constants(Class clazz) {
+            // Save the class name for debugging
+            mDataType = clazz.getSimpleName();
+
+            // Iterate through the list of fields on this class, and build the
+            // constant arrays for these fields.
+            final Field[] fields = clazz.getDeclaredFields();
+            final Class<Constant> annotationClass = Constant.class;
+
+            final int N = fields.length;
+
+            final SortedIntArray[] keys = new SortedIntArray[mKeys.length];
+            for (int i=0; i<keys.length; i++) {
+                keys[i] = new SortedIntArray(N);
+            }
+
+            for (int i=0; i<N; i++) {
+                final Field field = fields[i];
+                final Constant constant = field.getAnnotation(annotationClass);
+                if (constant != null) {
+                    final int type = constant.type();
+                    if (type >= keys.length) {
+                        throw new RuntimeException("Unknown Constant type " + type
+                                + " on " + field);
+                    }
+                    try {
+                        keys[type].addValue(field.getInt(null));
+                    } catch (IllegalAccessException ex) {
+                        throw new RuntimeException("Can't read constant value type=" + type
+                                + " field=" + field, ex);
+                    }
+                }
+            }
+
+            for (int i=0; i<keys.length; i++) {
+                mKeys[i] = keys[i].getArray();
+            }
+        }
+
+        /**
+         * Get a string representation of this class. Useful for debugging. It will be the
+         * simple name of the class passed in the constructor.
+         */
+        public String getDataType() {
+            return mDataType;
+        }
+
+        /**
+         * Return how many keys there are for the given field type.
+         *
+         * @see TYPE_TIMER
+         * @see TYPE_MEASUREMENT
+         * @see TYPE_TIMERS
+         * @see TYPE_MEASUREMENTS
+         * @see TYPE_STATS
+         */
+        public int getSize(int type) {
+            return mKeys[type].length;
+        }
+
+        /**
+         * Return the index for the given type and key combination in the array of field
+         * keys or values.
+         *
+         * @see TYPE_TIMER
+         * @see TYPE_MEASUREMENT
+         * @see TYPE_TIMERS
+         * @see TYPE_MEASUREMENTS
+         * @see TYPE_STATS
+         */
+        public int getIndex(int type, int key) {
+            final int index = Arrays.binarySearch(mKeys[type], key);
+            if (index >= 0) {
+                return index;
+            } else {
+                throw new RuntimeException("Unknown Constant " + key + " (of type "
+                        + type + " )");
+            }
+        }
+
+        /**
+         * Get the array of keys for the given field type.
+         */
+        public int[] getKeys(int type) {
+            return mKeys[type];
+        }
+    }
+
+    /**
+     * An array of fixed size that will be sorted.
+     */
+    private static class SortedIntArray {
+        int mCount;
+        int[] mArray;
+
+        /**
+         * Construct with the maximum number of values.
+         */
+        SortedIntArray(int maxCount) {
+            mArray = new int[maxCount];
+        }
+
+        /**
+         * Add a value.
+         */
+        void addValue(int value) {
+            mArray[mCount++] = value;
+        }
+
+        /**
+         * Get the array of values that have been added, with the values in
+         * numerically increasing order.
+         */
+        int[] getArray() {
+            if (mCount == mArray.length) {
+                Arrays.sort(mArray);
+                return mArray;
+            } else {
+                final int[] result = new int[mCount];
+                System.arraycopy(mArray, 0, result, 0, mCount);
+                Arrays.sort(result);
+                return result;
+            }
+        }
+    }
+}
+
+
diff --git a/android-34/android/os/health/HealthStats.java b/android-34/android/os/health/HealthStats.java
new file mode 100644
index 0000000..6c648f1
--- /dev/null
+++ b/android-34/android/os/health/HealthStats.java
@@ -0,0 +1,487 @@
+/*
+ * 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.os.health;
+
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * A HealthStats object contains system health data about an application.
+ *
+ * <p>
+ * <b>Data Types</b><br>
+ * Each of the keys references data in one of five data types:
+ *
+ * <p>
+ * A <b>measurement</b> metric contains a single {@code long} value. That value may
+ * be a count, a time, or some other type of value. The unit for a measurement
+ * (COUNT, MS, etc) will always be in the name of the constant for the key to
+ * retrieve it. For example, the
+ * {@link android.os.health.UidHealthStats#MEASUREMENT_WIFI_TX_MS UidHealthStats.MEASUREMENT_WIFI_TX_MS}
+ * value is the number of milliseconds (ms) that were spent transmitting on wifi by an
+ * application.  The
+ * {@link android.os.health.UidHealthStats#MEASUREMENT_MOBILE_RX_PACKETS UidHealthStats.MEASUREMENT_MOBILE_RX_PACKETS}
+ * measurement is the number of packets received on behalf of an application.
+ * The {@link android.os.health.UidHealthStats#MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT
+ *     UidHealthStats.MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT}
+ * measurement is the number of times the user touched the screen, causing the
+ * screen to stay awake.
+ *
+ *
+ * <p>
+ * A <b>timer</b> metric contains an {@code int} count and a {@code long} time,
+ * measured in milliseconds. Timers track how many times a resource was used, and
+ * the total duration for that usage. For example, the
+ * {@link android.os.health.UidHealthStats#TIMER_FLASHLIGHT}
+ * timer tracks how many times the application turned on the flashlight, and for
+ * how many milliseconds total it kept it on.
+ *
+ * <p>
+ * A <b>measurement map</b> metric is a mapping of {@link java.lang.String} names to
+ * {@link java.lang.Long} values.  The names typically are application provided names. For
+ * example, the
+ * {@link android.os.health.PackageHealthStats#MEASUREMENTS_WAKEUP_ALARMS_COUNT
+ *         PackageHealthStats.MEASUREMENTS_WAKEUP_ALARMS_COUNT}
+ * measurement map is a mapping of the tag provided to the
+ * {@link android.app.AlarmManager} when the alarm is scheduled.
+ *
+ * <p>
+ * A <b>timer map</b> metric is a mapping of {@link java.lang.String} names to
+ * {@link android.os.health.TimerStat} objects. The names are typically application
+ * provided names.  For example, the
+ * {@link android.os.health.UidHealthStats#TIMERS_WAKELOCKS_PARTIAL UidHealthStats.TIMERS_WAKELOCKS_PARTIAL}
+ * is a mapping of tag provided to the {@link android.os.PowerManager} when the
+ * wakelock is created to the number of times and for how long each wakelock was
+ * active.
+ *
+ * <p>
+ * Lastly, a <b>health stats</b> metric is a mapping of {@link java.lang.String}
+ * names to a recursive {@link android.os.health.HealthStats} object containing
+ * more detailed information. For example, the
+ * {@link android.os.health.UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES}
+ * metric is a mapping of the package names for each of the APKs sharing a uid to
+ * the information recorded for that apk.  The returned HealthStats objects will
+ * each be associated with a different set of constants.  For the HealthStats
+ * returned for UidHealthStats.STATS_PACKAGES, the keys come from the
+ * {@link android.os.health.PackageHealthStats}  class.
+ *
+ * <p>
+ * The keys that are available are subject to change, depending on what a particular
+ * device or software version is capable of recording. Applications must handle the absence of
+ * data without crashing.
+ */
+public class HealthStats {
+    // Header fields
+    private String mDataType;
+
+    // TimerStat fields
+    private int[] mTimerKeys;
+    private int[] mTimerCounts;
+    private long[] mTimerTimes;
+
+    // Measurement fields
+    private int[] mMeasurementKeys;
+    private long[] mMeasurementValues;
+
+    // Stats fields
+    private int[] mStatsKeys;
+    private ArrayMap<String,HealthStats>[] mStatsValues;
+
+    // Timers fields
+    private int[] mTimersKeys;
+    private ArrayMap<String,TimerStat>[] mTimersValues;
+
+    // Measurements fields
+    private int[] mMeasurementsKeys;
+    private ArrayMap<String,Long>[] mMeasurementsValues;
+
+    /**
+     * HealthStats empty constructor not implemented because this
+     * class is read-only.
+     */
+    private HealthStats() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /**
+     * Construct a health stats object from a parcel.
+     *
+     * @hide
+     */
+    @TestApi
+    public HealthStats(Parcel in) {
+        int count;
+
+        // Header fields
+        mDataType = in.readString();
+
+        // TimerStat fields
+        count = in.readInt();
+        mTimerKeys = new int[count];
+        mTimerCounts = new int[count];
+        mTimerTimes = new long[count];
+        for (int i=0; i<count; i++) {
+            mTimerKeys[i] = in.readInt();
+            mTimerCounts[i] = in.readInt();
+            mTimerTimes[i] = in.readLong();
+        }
+
+        // Measurement fields
+        count = in.readInt();
+        mMeasurementKeys = new int[count];
+        mMeasurementValues = new long[count];
+        for (int i=0; i<count; i++) {
+            mMeasurementKeys[i] = in.readInt();
+            mMeasurementValues[i] = in.readLong();
+        }
+
+        // Stats fields
+        count = in.readInt();
+        mStatsKeys = new int[count];
+        mStatsValues = new ArrayMap[count];
+        for (int i=0; i<count; i++) {
+            mStatsKeys[i] = in.readInt();
+            mStatsValues[i] = createHealthStatsMap(in);
+        }
+
+        // Timers fields
+        count = in.readInt();
+        mTimersKeys = new int[count];
+        mTimersValues = new ArrayMap[count];
+        for (int i=0; i<count; i++) {
+            mTimersKeys[i] = in.readInt();
+            mTimersValues[i] = createParcelableMap(in, TimerStat.CREATOR);
+        }
+
+        // Measurements fields
+        count = in.readInt();
+        mMeasurementsKeys = new int[count];
+        mMeasurementsValues = new ArrayMap[count];
+        for (int i=0; i<count; i++) {
+            mMeasurementsKeys[i] = in.readInt();
+            mMeasurementsValues[i] = createLongsMap(in);
+        }
+    }
+
+    /**
+     * Get a name representing the contents of this object.
+     *
+     * @see UidHealthStats
+     * @see PackageHealthStats
+     * @see PidHealthStats
+     * @see ProcessHealthStats
+     * @see ServiceHealthStats
+     */
+    public String getDataType() {
+        return mDataType;
+    }
+
+    /**
+     * Return whether this object contains a TimerStat for the supplied key.
+     */
+    public boolean hasTimer(int key) {
+        return getIndex(mTimerKeys, key) >= 0;
+    }
+
+    /**
+     * Return a TimerStat object for the given key.
+     *
+     * This will allocate a new {@link TimerStat} object, which may be wasteful. Instead, use
+     * {@link #getTimerCount} and {@link #getTimerTime}.
+     *
+     * @throws IndexOutOfBoundsException When the key is not present in this object.
+     * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+     */
+    public TimerStat getTimer(int key) {
+        final int index = getIndex(mTimerKeys, key);
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+                    + " key=" + key);
+        }
+        return new TimerStat(mTimerCounts[index], mTimerTimes[index]);
+    }
+
+    /**
+     * Get the count for the timer for the given key.
+     *
+     * @throws IndexOutOfBoundsException When the key is not present in this object.
+     * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+     */
+    public int getTimerCount(int key) {
+        final int index = getIndex(mTimerKeys, key);
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+                    + " key=" + key);
+        }
+        return mTimerCounts[index];
+    }
+
+    /**
+     * Get the time for the timer for the given key, in milliseconds.
+     *
+     * @throws IndexOutOfBoundsException When the key is not present in this object.
+     * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+     */
+    public long getTimerTime(int key) {
+        final int index = getIndex(mTimerKeys, key);
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+                    + " key=" + key);
+        }
+        return mTimerTimes[index];
+    }
+
+    /**
+     * Get the number of timer values in this object. Can be used to iterate through
+     * the available timers.
+     *
+     * @see #getTimerKeyAt
+     */
+    public int getTimerKeyCount() {
+        return mTimerKeys.length;
+    }
+
+    /**
+     * Get the key for the timer at the given index.  Index must be between 0 and the result
+     * of {@link #getTimerKeyCount getTimerKeyCount()}.
+     *
+     * @see #getTimerKeyCount
+     */
+    public int getTimerKeyAt(int index) {
+        return mTimerKeys[index];
+    }
+
+    /**
+     * Return whether this object contains a measurement for the supplied key.
+     */
+    public boolean hasMeasurement(int key) {
+        return getIndex(mMeasurementKeys, key) >= 0;
+    }
+
+    /**
+     * Get the measurement for the given key.
+     *
+     * @throws IndexOutOfBoundsException When the key is not present in this object.
+     * @see #hasMeasurement hasMeasurement(int) To check if a value for the given key is present.
+     */
+    public long getMeasurement(int key) {
+        final int index = getIndex(mMeasurementKeys, key);
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Bad measurement key dataType=" + mDataType
+                    + " key=" + key);
+        }
+        return mMeasurementValues[index];
+    }
+
+    /**
+     * Get the number of measurement values in this object. Can be used to iterate through
+     * the available measurements.
+     *
+     * @see #getMeasurementKeyAt
+     */
+    public int getMeasurementKeyCount() {
+        return mMeasurementKeys.length;
+    }
+
+    /**
+     * Get the key for the measurement at the given index.  Index must be between 0 and the result
+     * of {@link #getMeasurementKeyCount getMeasurementKeyCount()}.
+     *
+     * @see #getMeasurementKeyCount
+     */
+    public int getMeasurementKeyAt(int index) {
+        return mMeasurementKeys[index];
+    }
+
+    /**
+     * Return whether this object contains a HealthStats map for the supplied key.
+     */
+    public boolean hasStats(int key) {
+        return getIndex(mStatsKeys, key) >= 0;
+    }
+
+    /**
+     * Get the HealthStats map for the given key.
+     *
+     * @throws IndexOutOfBoundsException When the key is not present in this object.
+     * @see #hasStats hasStats(int) To check if a value for the given key is present.
+     */
+    public Map<String,HealthStats> getStats(int key) {
+        final int index = getIndex(mStatsKeys, key);
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Bad stats key dataType=" + mDataType
+                    + " key=" + key);
+        }
+        return mStatsValues[index];
+    }
+
+    /**
+     * Get the number of HealthStat map values in this object. Can be used to iterate through
+     * the available measurements.
+     *
+     * @see #getMeasurementKeyAt
+     */
+    public int getStatsKeyCount() {
+        return mStatsKeys.length;
+    }
+
+    /**
+     * Get the key for the timer at the given index.  Index must be between 0 and the result
+     * of {@link #getStatsKeyCount getStatsKeyCount()}.
+     *
+     * @see #getStatsKeyCount
+     */
+    public int getStatsKeyAt(int index) {
+        return mStatsKeys[index];
+    }
+
+    /**
+     * Return whether this object contains a timers map for the supplied key.
+     */
+    public boolean hasTimers(int key) {
+        return getIndex(mTimersKeys, key) >= 0;
+    }
+
+    /**
+     * Get the TimerStat map for the given key.
+     *
+     * @throws IndexOutOfBoundsException When the key is not present in this object.
+     * @see #hasTimers hasTimers(int) To check if a value for the given key is present.
+     */
+    public Map<String,TimerStat> getTimers(int key) {
+        final int index = getIndex(mTimersKeys, key);
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Bad timers key dataType=" + mDataType
+                    + " key=" + key);
+        }
+        return mTimersValues[index];
+    }
+
+    /**
+     * Get the number of timer map values in this object. Can be used to iterate through
+     * the available timer maps.
+     *
+     * @see #getTimersKeyAt
+     */
+    public int getTimersKeyCount() {
+        return mTimersKeys.length;
+    }
+
+    /**
+     * Get the key for the timer map at the given index.  Index must be between 0 and the result
+     * of {@link #getTimersKeyCount getTimersKeyCount()}.
+     *
+     * @see #getTimersKeyCount
+     */
+    public int getTimersKeyAt(int index) {
+        return mTimersKeys[index];
+    }
+
+    /**
+     * Return whether this object contains a measurements map for the supplied key.
+     */
+    public boolean hasMeasurements(int key) {
+        return getIndex(mMeasurementsKeys, key) >= 0;
+    }
+
+    /**
+     * Get the measurements map for the given key.
+     *
+     * @throws IndexOutOfBoundsException When the key is not present in this object.
+     * @see #hasMeasurements To check if a value for the given key is present.
+     */
+    public Map<String,Long> getMeasurements(int key) {
+        final int index = getIndex(mMeasurementsKeys, key);
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Bad measurements key dataType=" + mDataType
+                    + " key=" + key);
+        }
+        return mMeasurementsValues[index];
+    }
+
+    /**
+     * Get the number of measurement map values in this object. Can be used to iterate through
+     * the available measurement maps.
+     *
+     * @see #getMeasurementsKeyAt
+     */
+    public int getMeasurementsKeyCount() {
+        return mMeasurementsKeys.length;
+    }
+
+    /**
+     * Get the key for the measurement map at the given index.
+     * Index must be between 0 and the result
+     * of {@link #getMeasurementsKeyCount getMeasurementsKeyCount()}.
+     *
+     * @see #getMeasurementsKeyCount
+     */
+    public int getMeasurementsKeyAt(int index) {
+        return mMeasurementsKeys[index];
+    }
+
+    /**
+     * Get the index in keys of key.
+     */
+    private static int getIndex(int[] keys, int key) {
+        return Arrays.binarySearch(keys, key);
+    }
+
+    /**
+     * Create an ArrayMap<String,HealthStats> from the given Parcel.
+     */
+    private static ArrayMap<String,HealthStats> createHealthStatsMap(Parcel in) {
+        final int count = in.readInt();
+        final ArrayMap<String,HealthStats> result = new ArrayMap<String,HealthStats>(count);
+        for (int i=0; i<count; i++) {
+            result.put(in.readString(), new HealthStats(in));
+        }
+        return result;
+    }
+
+    /**
+     * Create an ArrayMap<String,T extends Parcelable> from the given Parcel using
+     * the given Parcelable.Creator.
+     */
+    private static <T extends Parcelable> ArrayMap<String,T> createParcelableMap(Parcel in,
+            Parcelable.Creator<T> creator) {
+        final int count = in.readInt();
+        final ArrayMap<String,T> result = new ArrayMap<String,T>(count);
+        for (int i=0; i<count; i++) {
+            result.put(in.readString(), creator.createFromParcel(in));
+        }
+        return result;
+    }
+
+    /**
+     * Create an ArrayMap<String,Long> from the given Parcel.
+     */
+    private static ArrayMap<String,Long> createLongsMap(Parcel in) {
+        final int count = in.readInt();
+        final ArrayMap<String,Long> result = new ArrayMap<String,Long>(count);
+        for (int i=0; i<count; i++) {
+            result.put(in.readString(), in.readLong());
+        }
+        return result;
+    }
+}
+
diff --git a/android-34/android/os/health/HealthStatsParceler.java b/android-34/android/os/health/HealthStatsParceler.java
new file mode 100644
index 0000000..eb864a4
--- /dev/null
+++ b/android-34/android/os/health/HealthStatsParceler.java
@@ -0,0 +1,88 @@
+/*
+ * 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.os.health;
+
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class to allow sending the HealthStats through aidl generated glue.
+ *
+ * The alternative would be to send a HealthStats object, which would
+ * require constructing one, and then immediately flattening it. This
+ * saves that step at the cost of doing the extra flattening when
+ * accessed in the same process as the writer.
+ *
+ * The HealthStatsWriter passed in the constructor is retained, so don't
+ * reuse them.
+ * @hide
+ */
+@TestApi
+public class HealthStatsParceler implements Parcelable {
+    private HealthStatsWriter mWriter;
+    private HealthStats mHealthStats;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final @android.annotation.NonNull Parcelable.Creator<HealthStatsParceler> CREATOR
+            = new Parcelable.Creator<HealthStatsParceler>() {
+        public HealthStatsParceler createFromParcel(Parcel in) {
+            return new HealthStatsParceler(in);
+        }
+
+        public HealthStatsParceler[] newArray(int size) {
+            return new HealthStatsParceler[size];
+        }
+    };
+
+    public HealthStatsParceler(HealthStatsWriter writer) {
+        mWriter = writer;
+    }
+
+    public HealthStatsParceler(Parcel in) {
+        mHealthStats = new HealthStats(in);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        // See comment on mWriter declaration above.
+        if (mWriter != null) {
+            mWriter.flattenToParcel(out);
+        } else {
+            throw new RuntimeException("Can not re-parcel HealthStatsParceler that was"
+                    + " constructed from a Parcel");
+        }
+    }
+
+    public HealthStats getHealthStats() {
+        if (mWriter != null) {
+            final Parcel parcel = Parcel.obtain();
+            mWriter.flattenToParcel(parcel);
+            parcel.setDataPosition(0);
+            mHealthStats = new HealthStats(parcel);
+            parcel.recycle();
+        }
+
+        return mHealthStats;
+    }
+}
+
diff --git a/android-34/android/os/health/HealthStatsWriter.java b/android-34/android/os/health/HealthStatsWriter.java
new file mode 100644
index 0000000..d4d10b0
--- /dev/null
+++ b/android-34/android/os/health/HealthStatsWriter.java
@@ -0,0 +1,290 @@
+/*
+ * 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.os.health;
+
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * Class to write the health stats data into a parcel, so it can then be
+ * retrieved via a {@link HealthStats} object.
+ *
+ * There is an attempt to keep this class as low overhead as possible, for
+ * example storing an int[] and a long[] instead of a TimerStat[].
+ *
+ * @hide
+ */
+@TestApi
+public class HealthStatsWriter {
+    private final HealthKeys.Constants mConstants;
+
+    // TimerStat fields
+    private final boolean[] mTimerFields;
+    private final int[] mTimerCounts;
+    private final long[] mTimerTimes;
+
+    // Measurement fields
+    private final boolean[] mMeasurementFields;
+    private final long[] mMeasurementValues;
+
+    // Stats fields
+    private final ArrayMap<String,HealthStatsWriter>[] mStatsValues;
+
+    // Timers fields
+    private final ArrayMap<String,TimerStat>[] mTimersValues;
+
+    // Measurements fields
+    private final ArrayMap<String,Long>[] mMeasurementsValues;
+
+    /**
+     * Construct a HealthStatsWriter object with the given constants.
+     *
+     * The "getDataType()" of the resulting HealthStats object will be the
+     * short name of the java class that the Constants object was initalized
+     * with.
+     */
+    public HealthStatsWriter(HealthKeys.Constants constants) {
+        mConstants = constants;
+
+        // TimerStat
+        final int timerCount = constants.getSize(HealthKeys.TYPE_TIMER);
+        mTimerFields = new boolean[timerCount];
+        mTimerCounts = new int[timerCount];
+        mTimerTimes = new long[timerCount];
+
+        // Measurement
+        final int measurementCount = constants.getSize(HealthKeys.TYPE_MEASUREMENT);
+        mMeasurementFields = new boolean[measurementCount];
+        mMeasurementValues = new long[measurementCount];
+
+        // Stats
+        final int statsCount = constants.getSize(HealthKeys.TYPE_STATS);
+        mStatsValues = new ArrayMap[statsCount];
+
+        // Timers
+        final int timersCount = constants.getSize(HealthKeys.TYPE_TIMERS);
+        mTimersValues = new ArrayMap[timersCount];
+
+        // Measurements
+        final int measurementsCount = constants.getSize(HealthKeys.TYPE_MEASUREMENTS);
+        mMeasurementsValues = new ArrayMap[measurementsCount];
+    }
+
+    /**
+     * Add a timer for the given key.
+     */
+    public void addTimer(int timerId, int count, long time) {
+        final int index = mConstants.getIndex(HealthKeys.TYPE_TIMER, timerId);
+
+        mTimerFields[index] = true;
+        mTimerCounts[index] = count;
+        mTimerTimes[index] = time;
+    }
+
+    /**
+     * Add a measurement for the given key.
+     */
+    public void addMeasurement(int measurementId, long value) {
+        final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENT, measurementId);
+
+        mMeasurementFields[index] = true;
+        mMeasurementValues[index] = value;
+    }
+
+    /**
+     * Add a recursive HealthStats object for the given key and string name. The value
+     * is stored as a HealthStatsWriter until this object is written to a parcel, so
+     * don't attempt to reuse the HealthStatsWriter.
+     *
+     * The value field should not be null.
+     */
+    public void addStats(int key, String name, HealthStatsWriter value) {
+        final int index = mConstants.getIndex(HealthKeys.TYPE_STATS, key);
+
+        ArrayMap<String,HealthStatsWriter> map = mStatsValues[index];
+        if (map == null) {
+            map = mStatsValues[index] = new ArrayMap<String,HealthStatsWriter>(1);
+        }
+        map.put(name, value);
+    }
+
+    /**
+     * Add a TimerStat for the given key and string name.
+     *
+     * The value field should not be null.
+     */
+    public void addTimers(int key, String name, TimerStat value) {
+        final int index = mConstants.getIndex(HealthKeys.TYPE_TIMERS, key);
+
+        ArrayMap<String,TimerStat> map = mTimersValues[index];
+        if (map == null) {
+            map = mTimersValues[index] = new ArrayMap<String,TimerStat>(1);
+        }
+        map.put(name, value);
+    }
+
+    /**
+     * Add a measurement for the given key and string name.
+     */
+    public void addMeasurements(int key, String name, long value) {
+        final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENTS, key);
+
+        ArrayMap<String,Long> map = mMeasurementsValues[index];
+        if (map == null) {
+            map = mMeasurementsValues[index] = new ArrayMap<String,Long>(1);
+        }
+        map.put(name, value);
+    }
+
+    /**
+     * Flattens the data in this HealthStatsWriter to the Parcel format
+     * that can be unparceled into a HealthStat.
+     * @more
+     * (Called flattenToParcel because this HealthStatsWriter itself is
+     * not parcelable and we don't flatten all the business about the
+     * HealthKeys.Constants, only the values that were actually supplied)
+     */
+    public void flattenToParcel(Parcel out) {
+        int[] keys;
+
+        // Header fields
+        out.writeString(mConstants.getDataType());
+
+        // TimerStat fields
+        out.writeInt(countBooleanArray(mTimerFields));
+        keys = mConstants.getKeys(HealthKeys.TYPE_TIMER);
+        for (int i=0; i<keys.length; i++) {
+            if (mTimerFields[i]) {
+                out.writeInt(keys[i]);
+                out.writeInt(mTimerCounts[i]);
+                out.writeLong(mTimerTimes[i]);
+            }
+        }
+
+        // Measurement fields
+        out.writeInt(countBooleanArray(mMeasurementFields));
+        keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENT);
+        for (int i=0; i<keys.length; i++) {
+            if (mMeasurementFields[i]) {
+                out.writeInt(keys[i]);
+                out.writeLong(mMeasurementValues[i]);
+            }
+        }
+
+        // Stats
+        out.writeInt(countObjectArray(mStatsValues));
+        keys = mConstants.getKeys(HealthKeys.TYPE_STATS);
+        for (int i=0; i<keys.length; i++) {
+            if (mStatsValues[i] != null) {
+                out.writeInt(keys[i]);
+                writeHealthStatsWriterMap(out, mStatsValues[i]);
+            }
+        }
+
+        // Timers
+        out.writeInt(countObjectArray(mTimersValues));
+        keys = mConstants.getKeys(HealthKeys.TYPE_TIMERS);
+        for (int i=0; i<keys.length; i++) {
+            if (mTimersValues[i] != null) {
+                out.writeInt(keys[i]);
+                writeParcelableMap(out, mTimersValues[i]);
+            }
+        }
+
+        // Measurements
+        out.writeInt(countObjectArray(mMeasurementsValues));
+        keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENTS);
+        for (int i=0; i<keys.length; i++) {
+            if (mMeasurementsValues[i] != null) {
+                out.writeInt(keys[i]);
+                writeLongsMap(out, mMeasurementsValues[i]);
+            }
+        }
+    }
+
+    /**
+     * Count how many of the fields have been set.
+     */
+    private static int countBooleanArray(boolean[] fields) {
+        int count = 0;
+        final int N = fields.length;
+        for (int i=0; i<N; i++) {
+            if (fields[i]) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Count how many of the fields have been set.
+     */
+    private static <T extends Object> int countObjectArray(T[] fields) {
+        int count = 0;
+        final int N = fields.length;
+        for (int i=0; i<N; i++) {
+            if (fields[i] != null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Write a map of String to HealthStatsWriter to the Parcel.
+     */
+    private static void writeHealthStatsWriterMap(Parcel out,
+            ArrayMap<String,HealthStatsWriter> map) {
+        final int N = map.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            out.writeString(map.keyAt(i));
+            map.valueAt(i).flattenToParcel(out);
+        }
+    }
+
+    /**
+     * Write a map of String to Parcelables to the Parcel.
+     */
+    private static <T extends Parcelable> void writeParcelableMap(Parcel out,
+            ArrayMap<String,T> map) {
+        final int N = map.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            out.writeString(map.keyAt(i));
+            map.valueAt(i).writeToParcel(out, 0);
+        }
+    }
+
+    /**
+     * Write a map of String to Longs to the Parcel.
+     */
+    private static void writeLongsMap(Parcel out, ArrayMap<String,Long> map) {
+        final int N = map.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            out.writeString(map.keyAt(i));
+            out.writeLong(map.valueAt(i));
+        }
+    }
+}
+
+
diff --git a/android-34/android/os/health/PackageHealthStats.java b/android-34/android/os/health/PackageHealthStats.java
new file mode 100644
index 0000000..fb52cb6
--- /dev/null
+++ b/android-34/android/os/health/PackageHealthStats.java
@@ -0,0 +1,50 @@
+/*
+ * 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.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES} key.
+ */
+public final class PackageHealthStats {
+
+    private PackageHealthStats() {
+    }
+
+    /**
+     * Key for a HealthStats with {@link ServiceHealthStats} keys for each of the
+     * services defined in this apk.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+    public static final int STATS_SERVICES = HealthKeys.BASE_PACKAGE + 1;
+
+    /**
+     * Key for a map of the number of times that a package's wakeup alarms have fired
+     * while the device was on battery.
+     *
+     * @see android.app.AlarmManager
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENTS)
+    public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = HealthKeys.BASE_PACKAGE + 2;
+
+    /**
+     * @hide
+     */
+    public static final HealthKeys.Constants CONSTANTS
+            = new HealthKeys.Constants(PackageHealthStats.class);
+}
diff --git a/android-34/android/os/health/PidHealthStats.java b/android-34/android/os/health/PidHealthStats.java
new file mode 100644
index 0000000..0d2a3f1
--- /dev/null
+++ b/android-34/android/os/health/PidHealthStats.java
@@ -0,0 +1,80 @@
+/*
+ * 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.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PIDS UidHealthStats.STATS_PIDS} key.
+ * <p>
+ * The values coming from PidHealthStats are a little bit different from
+ * the other HealthStats values.  These values are not aggregate or historical
+ * values, but instead live values from when the snapshot is taken.  These
+ * tend to be more useful in debugging rogue processes than in gathering
+ * aggregate metrics across the fleet of devices.
+ */
+public final class PidHealthStats {
+
+    private PidHealthStats() {
+    }
+
+    /**
+     * Key for a measurement of the current nesting depth of wakelocks for this process.
+     * That is to say, the number of times a nested wakelock has been started but not
+     * stopped.  A high number here indicates an improperly paired wakelock acquire/release
+     * combination.
+     * <p>
+     * More details on the individual wake locks is available
+     * by getting the {@link UidHealthStats#TIMERS_WAKELOCKS_FULL},
+     * {@link UidHealthStats#TIMERS_WAKELOCKS_PARTIAL},
+     * {@link UidHealthStats#TIMERS_WAKELOCKS_WINDOW}
+     * and {@link UidHealthStats#TIMERS_WAKELOCKS_DRAW} keys.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WAKE_NESTING_COUNT = HealthKeys.BASE_PID + 1;
+
+    /**
+     * Key for a measurement of the total number of milleseconds that this process
+     * has held a wake lock.
+     * <p>
+     * More details on the individual wake locks is available
+     * by getting the {@link UidHealthStats#TIMERS_WAKELOCKS_FULL},
+     * {@link UidHealthStats#TIMERS_WAKELOCKS_PARTIAL},
+     * {@link UidHealthStats#TIMERS_WAKELOCKS_WINDOW}
+     * and {@link UidHealthStats#TIMERS_WAKELOCKS_DRAW} keys.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WAKE_SUM_MS = HealthKeys.BASE_PID + 2;
+
+    /**
+     * Key for a measurement of the time in the {@link android.os.SystemClock#elapsedRealtime}
+     * timebase that a wakelock was first acquired in this process.
+     * <p>
+     * More details on the individual wake locks is available
+     * by getting the {@link UidHealthStats#TIMERS_WAKELOCKS_FULL},
+     * {@link UidHealthStats#TIMERS_WAKELOCKS_PARTIAL},
+     * {@link UidHealthStats#TIMERS_WAKELOCKS_WINDOW}
+     * and {@link UidHealthStats#TIMERS_WAKELOCKS_DRAW} keys.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WAKE_START_MS = HealthKeys.BASE_PID + 3;
+
+    /**
+     * @hide
+     */
+    public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(PidHealthStats.class);
+}
diff --git a/android-34/android/os/health/ProcessHealthStats.java b/android-34/android/os/health/ProcessHealthStats.java
new file mode 100644
index 0000000..b3f0dfc
--- /dev/null
+++ b/android-34/android/os/health/ProcessHealthStats.java
@@ -0,0 +1,72 @@
+/*
+ * 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.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PROCESSES UidHealthStats.STATS_PROCESSES} key.
+ */
+public final class ProcessHealthStats {
+
+    private ProcessHealthStats() {
+    }
+
+    /**
+     * Key for a measurement of number of millseconds the CPU spent running in user space
+     * for this process.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_USER_TIME_MS = HealthKeys.BASE_PROCESS + 1;
+
+    /**
+     * Key for a measurement of number of millseconds the CPU spent running in kernel space
+     * for this process.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_SYSTEM_TIME_MS = HealthKeys.BASE_PROCESS + 2;
+
+    /**
+     * Key for a measurement of the number of times this process was started for any reason.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_STARTS_COUNT = HealthKeys.BASE_PROCESS + 3;
+
+    /**
+     * Key for a measurement of the number of crashes that happened in this process.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_CRASHES_COUNT = HealthKeys.BASE_PROCESS + 4;
+
+    /**
+     * Key for a measurement of the number of ANRs that happened in this process.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_ANR_COUNT = HealthKeys.BASE_PROCESS + 5;
+
+    /**
+     * Key for a measurement of the number of milliseconds this process spent with
+     * an activity in the foreground.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_FOREGROUND_MS = HealthKeys.BASE_PROCESS + 6;
+
+    /**
+     * @hide
+     */
+    public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(ProcessHealthStats.class);
+}
diff --git a/android-34/android/os/health/ServiceHealthStats.java b/android-34/android/os/health/ServiceHealthStats.java
new file mode 100644
index 0000000..cc48b3e
--- /dev/null
+++ b/android-34/android/os/health/ServiceHealthStats.java
@@ -0,0 +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 android.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link PackageHealthStats#STATS_SERVICES PackageHealthStats.STATS_SERVICES} key.
+ */
+public final class ServiceHealthStats {
+
+    private ServiceHealthStats() {
+    }
+
+    /**
+     * Key for a measurement of the number of times this service was started due to calls to
+     * {@link android.content.Context#startService startService()}, including re-launches
+     * after crashes.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_START_SERVICE_COUNT = HealthKeys.BASE_SERVICE + 1;
+
+    /**
+     * Key for a measurement of the total number of times this service was started
+     * due to calls to {@link android.content.Context#startService startService()}
+     * or {@link android.content.Context#bindService bindService()} including re-launches
+     * after crashes.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_LAUNCH_COUNT = HealthKeys.BASE_SERVICE + 2;
+
+    /**
+     * @hide
+     */
+    public static final HealthKeys.Constants CONSTANTS
+            = new HealthKeys.Constants(ServiceHealthStats.class);
+}
diff --git a/android-34/android/os/health/SystemHealthManager.java b/android-34/android/os/health/SystemHealthManager.java
new file mode 100644
index 0000000..8181911
--- /dev/null
+++ b/android-34/android/os/health/SystemHealthManager.java
@@ -0,0 +1,140 @@
+/*
+ * 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.os.health;
+
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Build;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.app.IBatteryStats;
+
+/**
+ * Provides access to data about how various system resources are used by applications.
+ * @more
+ * <p>
+ * If you are going to be using this class to log your application's resource usage,
+ * please consider the amount of resources (battery, network, etc) that will be used
+ * by the logging itself.  It can be substantial.
+ * <p>
+ * <b>Battery Usage</b><br>
+ * Since Android version {@link android.os.Build.VERSION_CODES#Q}, the statistics related to power
+ * (battery) usage are recorded since the device was last considered fully charged (for previous
+ * versions, it is instead since the device was last unplugged).
+ * It is expected that applications schedule more work to do while the device is
+ * plugged in (e.g. using {@link android.app.job.JobScheduler JobScheduler}), and
+ * while that can affect charging rates, it is still preferable to actually draining
+ * the battery.
+ */
+@SystemService(Context.SYSTEM_HEALTH_SERVICE)
+public class SystemHealthManager {
+    private final IBatteryStats mBatteryStats;
+
+    /**
+     * Construct a new SystemHealthManager object.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public SystemHealthManager() {
+        this(IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME)));
+    }
+
+    /** {@hide} */
+    public SystemHealthManager(IBatteryStats batteryStats) {
+        mBatteryStats = batteryStats;
+    }
+
+    /**
+     * Obtain a SystemHealthManager object for the supplied context.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static SystemHealthManager from(Context context) {
+        return (SystemHealthManager)context.getSystemService(Context.SYSTEM_HEALTH_SERVICE);
+    }
+
+    /**
+     * Return a {@link HealthStats} object containing a snapshot of system health
+     * metrics for the given uid (user-id, which in usually corresponds to application).
+     * @more
+     *
+     * An application must hold the {@link android.Manifest.permission#BATTERY_STATS
+     * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats
+     * other than its own.
+     *
+     * @param uid User ID for a given application.
+     * @return A {@link HealthStats} object containing the metrics for the requested
+     * application. The keys for this HealthStats object will be from the {@link UidHealthStats}
+     * class.
+     * @see Process#myUid() Process.myUid()
+     */
+    public HealthStats takeUidSnapshot(int uid) {
+        try {
+            final HealthStatsParceler parceler = mBatteryStats.takeUidSnapshot(uid);
+            return parceler.getHealthStats();
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Return a {@link HealthStats} object containing a snapshot of system health
+     * metrics for the application calling this API. This method is the same as calling
+     * {@code takeUidSnapshot(Process.myUid())}.
+     *
+     * @return A {@link HealthStats} object containing the metrics for this application. The keys
+     * for this HealthStats object will be from the {@link UidHealthStats} class.
+     */
+    public HealthStats takeMyUidSnapshot() {
+        return takeUidSnapshot(Process.myUid());
+    }
+
+    /**
+     * Return a {@link HealthStats} object containing a snapshot of system health
+     * metrics for the given uids (user-id, which in usually corresponds to application).
+     * @more
+     *
+     * An application must hold the {@link android.Manifest.permission#BATTERY_STATS
+     * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats
+     * other than its own.
+     *
+     * @param uids An array of User IDs to retrieve.
+     * @return An array of {@link HealthStats} objects containing the metrics for each of
+     * the requested uids. The keys for this HealthStats object will be from the
+     * {@link UidHealthStats} class.
+     */
+    public HealthStats[] takeUidSnapshots(int[] uids) {
+        try {
+            final HealthStatsParceler[] parcelers = mBatteryStats.takeUidSnapshots(uids);
+            final HealthStats[] results = new HealthStats[uids.length];
+            final int N = uids.length;
+            for (int i=0; i<N; i++) {
+                results[i] = parcelers[i].getHealthStats();
+            }
+            return results;
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+}
+
diff --git a/android-34/android/os/health/TimerStat.java b/android-34/android/os/health/TimerStat.java
new file mode 100644
index 0000000..4aaa85f
--- /dev/null
+++ b/android-34/android/os/health/TimerStat.java
@@ -0,0 +1,116 @@
+/*
+ * 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.os.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A TimerStat object stores a count and a time.
+ *
+ * @more
+ * When possible, the other APIs in this package avoid requiring a TimerStat
+ * object to be constructed, even internally, but the getTimers method on
+ * {@link android.os.health.HealthStats} does require TimerStat objects.
+ */
+public final class TimerStat implements Parcelable {
+    private int mCount;
+    private long mTime;
+
+    /**
+     * The CREATOR instance for use by aidl Binder interfaces.
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<TimerStat> CREATOR
+            = new Parcelable.Creator<TimerStat>() {
+        public TimerStat createFromParcel(Parcel in) {
+            return new TimerStat(in);
+        }
+
+        public TimerStat[] newArray(int size) {
+            return new TimerStat[size];
+        }
+    };
+
+    /**
+     * Construct an empty TimerStat object with the count and time set to 0.
+     */
+    public TimerStat() {
+    }
+
+    /**
+     * Construct a TimerStat object with the supplied count and time fields.
+     *
+     * @param count The count
+     * @param time The time
+     */
+    public TimerStat(int count, long time) {
+        mCount = count;
+        mTime = time;
+    }
+
+    /**
+     * Construct a TimerStat object reading the values from a {@link android.os.Parcel Parcel}
+     * object.
+     */
+    public TimerStat(Parcel in) {
+        mCount = in.readInt();
+        mTime = in.readLong();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Write this TimerStat object to a parcel.
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCount);
+        out.writeLong(mTime);
+    }
+
+    /**
+     * Set the count for this timer.
+     */
+    public void setCount(int count) {
+        mCount = count;
+    }
+
+    /**
+     * Get the count for this timer.
+     */
+    public int getCount() {
+        return mCount;
+    }
+
+    /**
+     * Set the time for this timer in milliseconds.
+     */
+    public void setTime(long time) {
+        mTime = time;
+    }
+
+    /**
+     * Get the time for this timer in milliseconds.
+     */
+    public long getTime() {
+        return mTime;
+    }
+}
diff --git a/android-34/android/os/health/UidHealthStats.java b/android-34/android/os/health/UidHealthStats.java
new file mode 100644
index 0000000..488a542
--- /dev/null
+++ b/android-34/android/os/health/UidHealthStats.java
@@ -0,0 +1,460 @@
+/*
+ * 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.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link SystemHealthManager#takeUidSnapshot(int) SystemHealthManager.takeUidSnapshot(int)},
+ * {@link SystemHealthManager#takeMyUidSnapshot() SystemHealthManager.takeMyUidSnapshot()}, and
+ * {@link SystemHealthManager#takeUidSnapshots(int[]) SystemHealthManager.takeUidSnapshots(int[])}.
+ */
+public final class UidHealthStats {
+
+    private UidHealthStats() {
+    }
+
+    /**
+     * How many milliseconds this statistics report covers in wall-clock time while the
+     * device was on battery including both screen-on and screen-off time.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_REALTIME_BATTERY_MS = HealthKeys.BASE_UID + 1;
+
+    /**
+     * How many milliseconds this statistics report covers that the CPU was running while the
+     * device was on battery including both screen-on and screen-off time.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_UPTIME_BATTERY_MS = HealthKeys.BASE_UID + 2;
+
+    /**
+     * How many milliseconds this statistics report covers in wall-clock time while the
+     * device was on battery including only screen-off time.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 3;
+
+    /**
+     * How many milliseconds this statistics report covers that the CPU was running while the
+     * device was on battery including only screen-off time.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 4;
+
+    /**
+     * Key for a TimerStat for the times a
+     * {@link android.os.PowerManager#FULL_WAKE_LOCK full wake lock}
+     * was acquired for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+    public static final int TIMERS_WAKELOCKS_FULL = HealthKeys.BASE_UID + 5;
+
+    /**
+     * Key for a TimerStat for the times a
+     * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock}
+     * was acquired for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+    public static final int TIMERS_WAKELOCKS_PARTIAL = HealthKeys.BASE_UID + 6;
+
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+    public static final int TIMERS_WAKELOCKS_WINDOW = HealthKeys.BASE_UID + 7;
+
+    /**
+     * Key for a TimerStat for the times a system-defined wakelock was acquired
+     * to allow the application to draw when it otherwise would not be able to
+     * (e.g. on the lock screen or doze screen).
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+    public static final int TIMERS_WAKELOCKS_DRAW = HealthKeys.BASE_UID + 8;
+
+    /**
+     * Key for a map of Timers for the sync adapter syncs that were done for
+     * this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+    public static final int TIMERS_SYNCS = HealthKeys.BASE_UID + 9;
+
+    /**
+     * Key for a map of Timers for the {@link android.app.job.JobScheduler} jobs for
+     * this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+    public static final int TIMERS_JOBS = HealthKeys.BASE_UID + 10;
+
+    /**
+     * Key for a timer for the applications use of the GPS sensor.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_GPS_SENSOR = HealthKeys.BASE_UID + 11;
+
+    /**
+     * Key for a map of the sensor usage for this uid. The keys are a
+     * string representation of the handle for the sensor.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+    public static final int TIMERS_SENSORS = HealthKeys.BASE_UID + 12;
+
+    /**
+     * Key for a HealthStats with {@link PidHealthStats} keys for each of the
+     * currently running processes for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+    public static final int STATS_PIDS = HealthKeys.BASE_UID + 13;
+
+    /**
+     * Key for a HealthStats with {@link ProcessHealthStats} keys for each of the
+     * named processes for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+    public static final int STATS_PROCESSES = HealthKeys.BASE_UID + 14;
+
+    /**
+     * Key for a HealthStats with {@link PackageHealthStats} keys for each of the
+     * APKs that share this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+    public static final int STATS_PACKAGES = HealthKeys.BASE_UID + 15;
+
+    /**
+     * Key for a measurement of number of millseconds the wifi controller was
+     * idle but turned on on behalf of this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_IDLE_MS = HealthKeys.BASE_UID + 16;
+
+    /**
+     * Key for a measurement of number of millseconds the wifi transmitter was
+     * receiving data for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_RX_MS = HealthKeys.BASE_UID + 17;
+
+    /**
+     * Key for a measurement of number of millseconds the wifi transmitter was
+     * transmitting data for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_TX_MS = HealthKeys.BASE_UID + 18;
+
+    /**
+     * Key for a measurement of the estimated number of mA*ms used by this uid
+     * for wifi, that is to say the number of milliseconds of wifi activity
+     * times the mA current during that period.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_POWER_MAMS = HealthKeys.BASE_UID + 19;
+
+    /**
+     * Key for a measurement of number of millseconds the bluetooth controller was
+     * idle but turned on on behalf of this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = HealthKeys.BASE_UID + 20;
+
+    /**
+     * Key for a measurement of number of millseconds the bluetooth transmitter was
+     * receiving data for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_RX_MS = HealthKeys.BASE_UID + 21;
+
+    /**
+     * Key for a measurement of number of millseconds the bluetooth transmitter was
+     * transmitting data for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_TX_MS = HealthKeys.BASE_UID + 22;
+
+    /**
+     * Key for a measurement of the estimated number of mA*ms used by this uid
+     * for bluetooth, that is to say the number of milliseconds of activity
+     * times the mA current during that period.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = HealthKeys.BASE_UID + 23;
+
+    /**
+     * Key for a measurement of number of millseconds the mobile radio controller was
+     * idle but turned on on behalf of this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_IDLE_MS = HealthKeys.BASE_UID + 24;
+
+    /**
+     * Key for a measurement of number of millseconds the mobile radio transmitter was
+     * receiving data for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_RX_MS = HealthKeys.BASE_UID + 25;
+
+    /**
+     * Key for a measurement of number of millseconds the mobile radio transmitter was
+     * transmitting data for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_TX_MS = HealthKeys.BASE_UID + 26;
+
+    /**
+     * Key for a measurement of the estimated number of mA*ms used by this uid
+     * for mobile data, that is to say the number of milliseconds of activity
+     * times the mA current during that period.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_POWER_MAMS = HealthKeys.BASE_UID + 27;
+
+    /**
+     * Key for a measurement of number of millseconds the wifi controller was
+     * active on behalf of this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_RUNNING_MS = HealthKeys.BASE_UID + 28;
+
+    /**
+     * Key for a measurement of number of millseconds that this uid held a full wifi lock.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = HealthKeys.BASE_UID + 29;
+
+    /**
+     * Key for a timer for the count and duration of wifi scans done by this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_WIFI_SCAN = HealthKeys.BASE_UID + 30;
+
+    /**
+     * Key for a measurement of number of millseconds that this uid was performing
+     * multicast wifi traffic.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_MULTICAST_MS = HealthKeys.BASE_UID + 31;
+
+    /**
+     * Key for a timer for the count and duration of audio playback done by this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_AUDIO = HealthKeys.BASE_UID + 32;
+
+    /**
+     * Key for a timer for the count and duration of video playback done by this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_VIDEO = HealthKeys.BASE_UID + 33;
+
+    /**
+     * Key for a timer for the count and duration this uid had the flashlight turned on.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_FLASHLIGHT = HealthKeys.BASE_UID + 34;
+
+    /**
+     * Key for a timer for the count and duration this uid had the camera turned on.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_CAMERA = HealthKeys.BASE_UID + 35;
+
+    /**
+     * Key for a timer for the count and duration of when an activity from this uid
+     * was the foreground activitiy.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_FOREGROUND_ACTIVITY = HealthKeys.BASE_UID + 36;
+
+    /**
+     * Key for a timer for the count and duration of when this uid was doing bluetooth scans.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_BLUETOOTH_SCAN = HealthKeys.BASE_UID + 37;
+
+    /**
+     * Key for a timer for the count and duration of when this uid was in the "top" process state.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_PROCESS_STATE_TOP_MS = HealthKeys.BASE_UID + 38;
+
+    /**
+     * Key for a timer for the count and duration of when this uid was in the "foreground service"
+     * process state.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = HealthKeys.BASE_UID + 39;
+
+    /**
+     * Key for a timer for the count and duration of when this uid was in the "top sleeping"
+     * process state.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = HealthKeys.BASE_UID + 40;
+
+    /**
+     * Key for a timer for the count and duration of when this uid was in the "foreground"
+     * process state.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = HealthKeys.BASE_UID + 41;
+
+    /**
+     * Key for a timer for the count and duration of when this uid was in the "background"
+     * process state.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = HealthKeys.BASE_UID + 42;
+
+    /**
+     * Key for a timer for the count and duration of when this uid was in the "cached" process
+     * state.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_PROCESS_STATE_CACHED_MS = HealthKeys.BASE_UID + 43;
+
+    /**
+     * Key for a timer for the count and duration this uid had the vibrator turned on.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_VIBRATOR = HealthKeys.BASE_UID + 44;
+
+    /**
+     * Key for a measurement of number of software-generated user activity events caused
+     * by the UID.  Calls to userActivity() reset the user activity countdown timer and
+     * keep the screen on for the user's preferred screen-on setting.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 45;
+
+    /**
+     * Key for a measurement of number of user activity events due to physical button presses caused
+     * by the UID.  Calls to userActivity() reset the user activity countdown timer and
+     * keep the screen on for the user's preferred screen-on setting.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 46;
+
+    /**
+     * Key for a measurement of number of user activity events due to touch events caused
+     * by the UID.  Calls to userActivity() reset the user activity countdown timer and
+     * keep the screen on for the user's preferred screen-on setting.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 47;
+
+    /**
+     * Key for a measurement of number of bytes received for this uid by the mobile radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_RX_BYTES = HealthKeys.BASE_UID + 48;
+
+    /**
+     * Key for a measurement of number of bytes transmitted for this uid by the mobile radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_TX_BYTES = HealthKeys.BASE_UID + 49;
+
+    /**
+     * Key for a measurement of number of bytes received for this uid by the wifi radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_RX_BYTES = HealthKeys.BASE_UID + 50;
+
+    /**
+     * Key for a measurement of number of bytes transmitted for this uid by the wifi radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_TX_BYTES = HealthKeys.BASE_UID + 51;
+
+    /**
+     * Key for a measurement of number of bytes received for this uid by the bluetooth radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = HealthKeys.BASE_UID + 52;
+
+    /**
+     * Key for a measurement of number of bytes transmitted for this uid by the bluetooth radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = HealthKeys.BASE_UID + 53;
+
+    /**
+     * Key for a measurement of number of packets received for this uid by the mobile radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_RX_PACKETS = HealthKeys.BASE_UID + 54;
+
+    /**
+     * Key for a measurement of number of packets transmitted for this uid by the mobile radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_MOBILE_TX_PACKETS = HealthKeys.BASE_UID + 55;
+
+    /**
+     * Key for a measurement of number of packets received for this uid by the wifi radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_RX_PACKETS = HealthKeys.BASE_UID + 56;
+
+    /**
+     * Key for a measurement of number of packets transmitted for this uid by the wifi radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_WIFI_TX_PACKETS = HealthKeys.BASE_UID + 57;
+
+    /**
+     * Key for a measurement of number of packets received for this uid by the bluetooth radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = HealthKeys.BASE_UID + 58;
+
+    /**
+     * Key for a measurement of number of packets transmitted for this uid by the bluetooth radio.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = HealthKeys.BASE_UID + 59;
+
+    /**
+     * Key for a timer for the count and duration the mobile radio was turned on for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+    public static final int TIMER_MOBILE_RADIO_ACTIVE = HealthKeys.BASE_UID + 61;
+
+    /**
+     * Key for a measurement of the number of milliseconds spent by the CPU running user space
+     * code for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_USER_CPU_TIME_MS = HealthKeys.BASE_UID + 62;
+
+    /**
+     * Key for a measurement of the number of milliseconds spent by the CPU running kernel
+     * code for this uid.
+     */
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_SYSTEM_CPU_TIME_MS = HealthKeys.BASE_UID + 63;
+
+    /**
+     * An estimate of the number of milliamp-microsends used by this uid.
+     *
+     * @deprecated this measurement is vendor-dependent and not reliable.
+     */
+    @Deprecated
+    @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+    public static final int MEASUREMENT_CPU_POWER_MAMS = HealthKeys.BASE_UID + 64;
+
+    /**
+     * @hide
+     */
+    public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(UidHealthStats.class);
+}
+
diff --git a/android-34/android/os/image/DynamicSystemClient.java b/android-34/android/os/image/DynamicSystemClient.java
new file mode 100644
index 0000000..218ecc8
--- /dev/null
+++ b/android-34/android/os/image/DynamicSystemClient.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.image;
+
+import android.annotation.BytesLong;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelableException;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * <p>This class contains methods and constants used to start a {@code DynamicSystem} installation,
+ * and a listener for status updates.</p>
+ *
+ * <p>{@code DynamicSystem} allows users to run certified system images in a non destructive manner
+ * without needing to prior OEM unlock. It creates a temporary system partition to install the new
+ * system image, and a temporary data partition for the newly installed system to run with.</p>
+ *
+ * After the installation is completed, the device will be running in the new system on next the
+ * reboot. Then, when the user reboots the device again, it will leave {@code DynamicSystem} and go
+ * back to the original system. While running in {@code DynamicSystem}, persitent storage for
+ * factory reset protection (FRP) remains unchanged. Since the user is running the new system with
+ * a temporarily created data partition, their original user data are kept unchanged.</p>
+ *
+ * <p>With {@link #setOnStatusChangedListener}, API users can register an
+ * {@link #OnStatusChangedListener} to get status updates and their causes when the installation is
+ * started, stopped, or cancelled. It also sends progress updates during the installation. With
+ * {@link #start}, API users can start an installation with the {@link Uri} to a unsparsed and
+ * gzipped system image. The {@link Uri} can be a web URL or a content Uri to a local path.</p>
+ *
+ * @hide
+ */
+@SystemApi
+public class DynamicSystemClient {
+    private static final String TAG = "DynamicSystemClient";
+
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_UNKNOWN,
+            STATUS_NOT_STARTED,
+            STATUS_IN_PROGRESS,
+            STATUS_READY,
+            STATUS_IN_USE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallationStatus {}
+
+    /** @hide */
+    @IntDef(prefix = { "CAUSE_" }, value = {
+            CAUSE_NOT_SPECIFIED,
+            CAUSE_INSTALL_COMPLETED,
+            CAUSE_INSTALL_CANCELLED,
+            CAUSE_ERROR_IO,
+            CAUSE_ERROR_INVALID_URL,
+            CAUSE_ERROR_IPC,
+            CAUSE_ERROR_EXCEPTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusChangedCause {}
+
+    /** Listener for installation status updates. */
+    public interface OnStatusChangedListener {
+        /**
+         * This callback is called when installation status is changed, and when the
+         * client is {@link #bind} to {@code DynamicSystem} installation service.
+         *
+         * @param status status code, also defined in {@code DynamicSystemClient}.
+         * @param cause cause code, also defined in {@code DynamicSystemClient}.
+         * @param progress number of bytes installed.
+         * @param detail additional detail about the error if available, otherwise null.
+         */
+        void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause,
+                @BytesLong long progress, @Nullable Throwable detail);
+    }
+
+    /*
+     * Status codes
+     */
+    /** We are bound to installation service, but failed to get its status */
+    public static final int STATUS_UNKNOWN = 0;
+
+    /** Installation is not started yet. */
+    public static final int STATUS_NOT_STARTED = 1;
+
+    /** Installation is in progress. */
+    public static final int STATUS_IN_PROGRESS = 2;
+
+    /** Installation is finished but the user has not launched it. */
+    public static final int STATUS_READY = 3;
+
+    /** Device is running in {@code DynamicSystem}. */
+    public static final int STATUS_IN_USE = 4;
+
+    /*
+     * Causes
+     */
+    /** Cause is not specified. This means the status is not changed. */
+    public static final int CAUSE_NOT_SPECIFIED = 0;
+
+    /** Status changed because installation is completed. */
+    public static final int CAUSE_INSTALL_COMPLETED = 1;
+
+    /** Status changed because installation is cancelled. */
+    public static final int CAUSE_INSTALL_CANCELLED = 2;
+
+    /** Installation failed due to {@code IOException}. */
+    public static final int CAUSE_ERROR_IO = 3;
+
+    /** Installation failed because the image URL source is not supported. */
+    public static final int CAUSE_ERROR_INVALID_URL = 4;
+
+    /** Installation failed due to IPC error. */
+    public static final int CAUSE_ERROR_IPC = 5;
+
+    /** Installation failed due to unhandled exception. */
+    public static final int CAUSE_ERROR_EXCEPTION = 6;
+
+    /*
+     * IPC Messages
+     */
+    /**
+     * Message to register listener.
+     * @hide
+     */
+    public static final int MSG_REGISTER_LISTENER = 1;
+
+    /**
+     * Message to unregister listener.
+     * @hide
+     */
+    public static final int MSG_UNREGISTER_LISTENER = 2;
+
+    /**
+     * Message for status updates.
+     * @hide
+     */
+    public static final int MSG_POST_STATUS = 3;
+
+    /*
+     * Messages keys
+     */
+    /**
+     * Message key, for progress updates.
+     * @hide
+     */
+    public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE";
+
+    /**
+     * Message key, used when the service is sending exception detail to the client.
+     * @hide
+     */
+    public static final String KEY_EXCEPTION_DETAIL = "KEY_EXCEPTION_DETAIL";
+
+    /*
+     * Intent Actions
+     */
+    /**
+     * Intent action: start installation.
+     * @hide
+     */
+    public static final String ACTION_START_INSTALL =
+            "android.os.image.action.START_INSTALL";
+
+    /**
+     * Intent action: notify user if we are currently running in {@code DynamicSystem}.
+     * @hide
+     */
+    public static final String ACTION_NOTIFY_IF_IN_USE =
+            "android.os.image.action.NOTIFY_IF_IN_USE";
+
+    /**
+     * Intent action: hide notifications about the status of {@code DynamicSystem}.
+     * @hide
+     */
+    public static final String ACTION_HIDE_NOTIFICATION =
+            "android.os.image.action.HIDE_NOTIFICATION";
+
+    /*
+     * Intent Keys
+     */
+    /**
+     * Intent key: Size of the system image, in bytes.
+     * @hide
+     */
+    public static final String KEY_SYSTEM_SIZE = "KEY_SYSTEM_SIZE";
+
+    /**
+     * Intent key: Number of bytes to reserve for userdata.
+     * @hide
+     */
+    public static final String KEY_USERDATA_SIZE = "KEY_USERDATA_SIZE";
+
+    /**
+     * Intent key: Whether to enable DynamicSystem immediately after installation is done.
+     *             Note this will reboot the device automatically.
+     * @hide
+     */
+    public static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
+
+    /**
+     * Intent key: Whether to leave DynamicSystem on device reboot.
+     *             False indicates a sticky mode where device stays in DynamicSystem across reboots.
+     * @hide
+     */
+    public static final String KEY_ONE_SHOT = "KEY_ONE_SHOT";
+
+    /**
+     * Intent key: Whether to use default strings when showing the dialog that prompts
+     *             user for device credentials.
+     *             False indicates using the custom strings provided by {@code DynamicSystem}.
+     * @hide
+     */
+    public static final String KEY_KEYGUARD_USE_DEFAULT_STRINGS =
+            "KEY_KEYGUARD_USE_DEFAULT_STRINGS";
+
+    private static class IncomingHandler extends Handler {
+        private final WeakReference<DynamicSystemClient> mWeakClient;
+
+        IncomingHandler(DynamicSystemClient service) {
+            super(Looper.getMainLooper());
+            mWeakClient = new WeakReference<>(service);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            DynamicSystemClient service = mWeakClient.get();
+
+            if (service != null) {
+                service.handleMessage(msg);
+            }
+        }
+    }
+
+    private class DynSystemServiceConnection implements ServiceConnection {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Slog.v(TAG, "onServiceConnected: " + className);
+
+            mService = new Messenger(service);
+
+            try {
+                Message msg = Message.obtain(null, MSG_REGISTER_LISTENER);
+                msg.replyTo = mMessenger;
+
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to get status from installation service");
+                notifyOnStatusChangedListener(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Slog.v(TAG, "onServiceDisconnected: " + className);
+            mService = null;
+        }
+    }
+
+    private final Context mContext;
+    private final DynSystemServiceConnection mConnection;
+    private final Messenger mMessenger;
+
+    private boolean mBound;
+    private Executor mExecutor;
+    private OnStatusChangedListener mListener;
+    private Messenger mService;
+
+    /**
+     * Create a new {@code DynamicSystem} client.
+     *
+     * @param context a {@link Context} will be used to bind the installation service.
+     *
+     * @hide
+     */
+    @SystemApi
+    public DynamicSystemClient(@NonNull Context context) {
+        mContext = context;
+        mConnection = new DynSystemServiceConnection();
+        mMessenger = new Messenger(new IncomingHandler(this));
+    }
+
+    /**
+     * This method register a listener for status change. The listener is called using
+     * the executor.
+     */
+    public void setOnStatusChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnStatusChangedListener listener) {
+        mListener = listener;
+        mExecutor = executor;
+    }
+
+    /**
+     * This method register a listener for status change. The listener is called in main
+     * thread.
+     */
+    public void setOnStatusChangedListener(
+            @NonNull OnStatusChangedListener listener) {
+        mListener = listener;
+        mExecutor = null;
+    }
+
+    private void notifyOnStatusChangedListener(
+            int status, int cause, long progress, Throwable detail) {
+        if (mListener != null) {
+            if (mExecutor != null) {
+                mExecutor.execute(
+                        () -> {
+                            mListener.onStatusChanged(status, cause, progress, detail);
+                        });
+            } else {
+                mListener.onStatusChanged(status, cause, progress, detail);
+            }
+        }
+    }
+
+    /**
+     * Bind to {@code DynamicSystem} installation service. Binding to the installation service
+     * allows it to send status updates to {@link #OnStatusChangedListener}. It is recommanded
+     * to bind before calling {@link #start} and get status updates.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
+    @SystemApi
+    public void bind() {
+        Intent intent = new Intent();
+        intent.setClassName("com.android.dynsystem",
+                "com.android.dynsystem.DynamicSystemInstallationService");
+
+        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
+        mBound = true;
+    }
+
+    /**
+     * Unbind from {@code DynamicSystem} installation service. Unbinding from the installation
+     * service stops it from sending following status updates.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
+    @SystemApi
+    public void unbind() {
+        if (!mBound) {
+            return;
+        }
+
+        if (mService != null) {
+            try {
+                Message msg = Message.obtain(null, MSG_UNREGISTER_LISTENER);
+                msg.replyTo = mMessenger;
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to unregister from installation service");
+            }
+        }
+
+        // Detach our existing connection.
+        mContext.unbindService(mConnection);
+
+        mBound = false;
+    }
+
+    /**
+     * Start installing {@code DynamicSystem} from URL with default userdata size.
+     *
+     * Calling this function will first start an Activity to confirm device credential, using
+     * {@link KeyguardManager}. If it's confirmed, the installation service will be started.
+     *
+     * This function doesn't require prior calling {@link #bind}.
+     *
+     * @param systemUrl a network Uri, a file Uri or a content Uri pointing to a system image file.
+     * @param systemSize size of system image.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
+    @SystemApi
+    public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) {
+        start(systemUrl, systemSize, 0 /* Use the default userdata size */);
+    }
+
+    /**
+     * Start installing {@code DynamicSystem} from URL.
+     *
+     * Calling this function will first start an Activity to confirm device credential, using
+     * {@link KeyguardManager}. If it's confirmed, the installation service will be started.
+     *
+     * This function doesn't require prior calling {@link #bind}.
+     *
+     * @param systemUrl a network Uri, a file Uri or a content Uri pointing to a system image file.
+     * @param systemSize size of system image.
+     * @param userdataSize bytes reserved for userdata.
+     */
+    @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
+    public void start(@NonNull Uri systemUrl, @BytesLong long systemSize,
+            @BytesLong long userdataSize) {
+        Intent intent = new Intent();
+
+        intent.setClassName("com.android.dynsystem",
+                "com.android.dynsystem.VerificationActivity");
+
+        intent.setData(systemUrl);
+        intent.setAction(ACTION_START_INSTALL);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
+        intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
+
+        mContext.startActivity(intent);
+    }
+
+    private void handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_POST_STATUS:
+                int status = msg.arg1;
+                int cause = msg.arg2;
+                // obj is non-null
+                Bundle bundle = (Bundle) msg.obj;
+                long progress = bundle.getLong(KEY_INSTALLED_SIZE);
+                ParcelableException t = (ParcelableException) bundle.getSerializable(
+                        KEY_EXCEPTION_DETAIL, android.os.ParcelableException.class);
+
+                Throwable detail = t == null ? null : t.getCause();
+
+                notifyOnStatusChangedListener(status, cause, progress, detail);
+                break;
+            default:
+                // do nothing
+
+        }
+    }
+}
diff --git a/android-34/android/os/image/DynamicSystemManager.java b/android-34/android/os/image/DynamicSystemManager.java
new file mode 100644
index 0000000..9610b16
--- /dev/null
+++ b/android-34/android/os/image/DynamicSystemManager.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.image;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.gsi.AvbPublicKey;
+import android.gsi.GsiProgress;
+import android.gsi.IGsiService;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Pair;
+
+/**
+ * The DynamicSystemManager offers a mechanism to use a new system image temporarily. After the
+ * installation, the device can reboot into this image with a new created /data. This image will
+ * last until the next reboot and then the device will go back to the original image. However the
+ * installed image and the new created /data are not deleted but disabled. Thus the application can
+ * either re-enable the installed image by calling {@link #toggle} or use the {@link #remove} to
+ * delete it completely. In other words, there are three device states: no installation, installed
+ * and running. The procedure to install a DynamicSystem starts with a {@link #startInstallation},
+ * followed by a series of {@link #write} and ends with a {@link commit}. Once the installation is
+ * complete, the device state changes from no installation to the installed state and a followed
+ * reboot will change its state to running. Note one instance of DynamicSystem can exist on a given
+ * device thus the {@link #startInstallation} will fail if the device is currently running a
+ * DynamicSystem.
+ *
+ * @hide
+ */
+@SystemService(Context.DYNAMIC_SYSTEM_SERVICE)
+public class DynamicSystemManager {
+    private static final String TAG = "DynamicSystemManager";
+
+    private final IDynamicSystemService mService;
+
+    /** {@hide} */
+    public DynamicSystemManager(IDynamicSystemService service) {
+        mService = service;
+    }
+
+    /** The DynamicSystemManager.Session represents a started session for the installation. */
+    public class Session {
+        private Session() {}
+
+        /**
+         * Set the file descriptor that points to a ashmem which will be used
+         * to fetch data during the submitFromAshmem.
+         *
+         * @param ashmem fd that points to a ashmem
+         * @param size size of the ashmem file
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+        public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
+            try {
+                return mService.setAshmem(ashmem, size);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+
+        /**
+         * Submit bytes to the DSU partition from the ashmem previously set with
+         * setAshmem.
+         *
+         * @param size Number of bytes
+         * @return true on success, false otherwise.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+        public boolean submitFromAshmem(int size) {
+            try {
+                return mService.submitFromAshmem(size);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+
+        /**
+         * Retrieve AVB public key from installing partition.
+         *
+         * @param dst           Output the AVB public key.
+         * @return              true on success, false if partition doesn't have a
+         *                      valid VBMeta block to retrieve the AVB key from.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+        public boolean getAvbPublicKey(AvbPublicKey dst) {
+            try {
+                return mService.getAvbPublicKey(dst);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+
+        /**
+         * Finish write and make device to boot into the it after reboot.
+         *
+         * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
+         *     error.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+        public boolean commit() {
+            try {
+                return mService.setEnable(true, true);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+    }
+    /**
+     * Start DynamicSystem installation.
+     *
+     * @return true if the call succeeds
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean startInstallation(String dsuSlot) {
+        try {
+            return mService.startInstallation(dsuSlot);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+    /**
+     * Start DynamicSystem installation. This call may take an unbounded amount of time. The caller
+     * may use another thread to call the getStartProgress() to get the progress.
+     *
+     * @param name The DSU partition name
+     * @param size Size of the DSU image in bytes
+     * @param readOnly True if the partition is read only, e.g. system.
+     * @return {@code Integer} an IGsiService.INSTALL_* status code. {@link Session} an installation
+     *     session object if successful, otherwise {@code null}.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public @NonNull Pair<Integer, Session> createPartition(
+            String name, long size, boolean readOnly) {
+        try {
+            int status = mService.createPartition(name, size, readOnly);
+            if (status == IGsiService.INSTALL_OK) {
+                return new Pair<>(status, new Session());
+            } else {
+                return new Pair<>(status, null);
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+    /**
+     * Complete the current partition installation.
+     *
+     * @return true if the partition installation completes without error.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean closePartition() {
+        try {
+            return mService.closePartition();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+    /**
+     * Finish a previously started installation. Installations without a cooresponding
+     * finishInstallation() will be cleaned up during device boot.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean finishInstallation() {
+        try {
+            return mService.finishInstallation();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+    /**
+     * Query the progress of the current installation operation. This can be called while the
+     * installation is in progress.
+     *
+     * @return GsiProgress GsiProgress { int status; long bytes_processed; long total_bytes; } The
+     *     status field can be IGsiService.STATUS_NO_OPERATION, IGsiService.STATUS_WORKING or
+     *     IGsiService.STATUS_COMPLETE.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public GsiProgress getInstallationProgress() {
+        try {
+            return mService.getInstallationProgress();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Abort the installation process. Note this method must be called in a thread other than the
+     * one calling the startInstallation method as the startInstallation method will not return
+     * until it is finished.
+     *
+     * @return {@code true} if the call succeeds. {@code false} if there is no installation
+     *     currently.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean abort() {
+        try {
+            return mService.abort();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /** @return {@code true} if the device is running a dynamic system */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean isInUse() {
+        try {
+            return mService.isInUse();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /** @return {@code true} if the device has a dynamic system installed */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean isInstalled() {
+        try {
+            return mService.isInstalled();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /** @return {@code true} if the device has a dynamic system enabled */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean isEnabled() {
+        try {
+            return mService.isEnabled();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Remove DynamicSystem installation if present
+     *
+     * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean remove() {
+        try {
+            return mService.remove();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Enable or disable DynamicSystem.
+     * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean setEnable(boolean enable, boolean oneShot) {
+        try {
+            return mService.setEnable(enable, oneShot);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    /**
+     * Returns the suggested scratch partition size for overlayFS.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public long suggestScratchSize() {
+        try {
+            return mService.suggestScratchSize();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+}
diff --git a/android-34/android/os/incremental/IncrementalFileStorages.java b/android-34/android/os/incremental/IncrementalFileStorages.java
new file mode 100644
index 0000000..6f4a12f
--- /dev/null
+++ b/android-34/android/os/incremental/IncrementalFileStorages.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+/**
+ * Set up files and directories used in an installation session. Currently only used by Incremental
+ * Installation. For Incremental installation, the expected outcome of this function is: 0) All the
+ * files are in defaultStorage 1) All APK files are in the same directory, bound to mApkStorage, and
+ * bound to the InstallerSession's stage dir. The files are linked from mApkStorage to
+ * defaultStorage. 2) All lib files are in the sub directories as their names suggest, and in the
+ * same parent directory as the APK files. The files are linked from mApkStorage to defaultStorage.
+ * 3) OBB files are in another directory that is different from APK files and lib files, bound to
+ * mObbStorage. The files are linked from mObbStorage to defaultStorage.
+ *
+ * @throws IllegalStateException the session is not an Incremental installation session.
+ */
+
+import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.IPackageLoadingProgressCallback;
+import android.content.pm.InstallationFileParcel;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * This class manages storage instances used during a package installation session.
+ * @hide
+ */
+public final class IncrementalFileStorages {
+    private static final String TAG = "IncrementalFileStorages";
+
+    private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
+
+    private @NonNull final IncrementalManager mIncrementalManager;
+    private @NonNull final File mStageDir;
+    private @Nullable IncrementalStorage mInheritedStorage;
+    private @Nullable IncrementalStorage mDefaultStorage;
+
+    /**
+     * Set up files and directories used in an installation session. Only used by Incremental.
+     * All the files will be created in defaultStorage.
+     *
+     * @throws IllegalStateException the session is not an Incremental installation session.
+     * @throws IOException if fails to setup files or directories.
+     */
+    public static IncrementalFileStorages initialize(Context context,
+            @NonNull File stageDir,
+            @Nullable File inheritedDir,
+            @NonNull DataLoaderParams dataLoaderParams,
+            @Nullable IDataLoaderStatusListener statusListener,
+            @Nullable StorageHealthCheckParams healthCheckParams,
+            @Nullable IStorageHealthListener healthListener,
+            @NonNull List<InstallationFileParcel> addedFiles,
+            @NonNull PerUidReadTimeouts[] perUidReadTimeouts,
+            @Nullable IPackageLoadingProgressCallback progressCallback) throws IOException {
+        IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
+                Context.INCREMENTAL_SERVICE);
+        if (incrementalManager == null) {
+            throw new IOException("Failed to obtain incrementalManager.");
+        }
+
+        final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, inheritedDir,
+                incrementalManager, dataLoaderParams);
+        for (InstallationFileParcel file : addedFiles) {
+            if (file.location == LOCATION_DATA_APP) {
+                try {
+                    result.addApkFile(file);
+                } catch (IOException e) {
+                    throw new IOException(
+                            "Failed to add file to IncFS: " + file.name + ", reason: ", e);
+                }
+            } else {
+                throw new IOException("Unknown file location: " + file.location);
+            }
+        }
+        // Register progress loading callback after files have been added
+        if (progressCallback != null) {
+            incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(),
+                    progressCallback);
+        }
+        result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener,
+                perUidReadTimeouts);
+
+        return result;
+    }
+
+    private IncrementalFileStorages(@NonNull File stageDir,
+            @Nullable File inheritedDir,
+            @NonNull IncrementalManager incrementalManager,
+            @NonNull DataLoaderParams dataLoaderParams) throws IOException {
+        try {
+            mStageDir = stageDir;
+            mIncrementalManager = incrementalManager;
+            if (inheritedDir != null && IncrementalManager.isIncrementalPath(
+                    inheritedDir.getAbsolutePath())) {
+                mInheritedStorage = mIncrementalManager.openStorage(
+                        inheritedDir.getAbsolutePath());
+                if (mInheritedStorage != null) {
+                    boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+                            dataLoaderParams.getComponentName().getPackageName());
+                    if (systemDataLoader && !mInheritedStorage.isFullyLoaded()) {
+                        // System data loader does not support incomplete storages.
+                        throw new IOException("Inherited storage has missing pages.");
+                    }
+
+                    mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
+                            mInheritedStorage, IncrementalManager.CREATE_MODE_CREATE
+                                    | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+                    if (mDefaultStorage == null) {
+                        throw new IOException(
+                                "Couldn't create linked incremental storage at " + stageDir);
+                    }
+                    return;
+                }
+            }
+
+            mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
+                    dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
+                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+            if (mDefaultStorage == null) {
+                throw new IOException(
+                        "Couldn't create incremental storage at " + stageDir);
+            }
+        } catch (IOException e) {
+            cleanUp();
+            throw e;
+        }
+    }
+
+    private void addApkFile(@NonNull InstallationFileParcel apk) throws IOException {
+        final String apkName = apk.name;
+        final File targetFile = new File(mStageDir, apkName);
+        if (!targetFile.exists()) {
+            mDefaultStorage.makeFile(apkName, apk.size, 0777, null, apk.metadata,
+                    apk.signature, null);
+        }
+    }
+
+    /**
+     * Starts or re-starts loading of data.
+     */
+    public void startLoading(
+            @NonNull DataLoaderParams dataLoaderParams,
+            @Nullable IDataLoaderStatusListener statusListener,
+            @Nullable StorageHealthCheckParams healthCheckParams,
+            @Nullable IStorageHealthListener healthListener,
+            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
+        if (!mDefaultStorage.startLoading(dataLoaderParams, statusListener, healthCheckParams,
+                healthListener, perUidReadTimeouts)) {
+            throw new IOException(
+                    "Failed to start or restart loading data for Incremental installation.");
+        }
+    }
+
+    /**
+     * Creates file in default storage and sets its content.
+     */
+    public void makeFile(@NonNull String name, @NonNull byte[] content,
+            @NonNull int mode) throws IOException {
+        mDefaultStorage.makeFile(name, content.length, mode, UUID.randomUUID(),
+                null, null, content);
+    }
+
+    /**
+     * Creates a hardlink from inherited storage to default.
+     */
+    public boolean makeLink(@NonNull String relativePath, @NonNull String fromBase,
+            @NonNull String toBase) throws IOException {
+        if (mInheritedStorage == null) {
+            return false;
+        }
+        final File sourcePath = new File(fromBase, relativePath);
+        final File destPath = new File(toBase, relativePath);
+        mInheritedStorage.makeLink(sourcePath.getAbsolutePath(), mDefaultStorage,
+                destPath.getAbsolutePath());
+        return true;
+    }
+
+    /**
+     * Permanently disables readlogs.
+     */
+    public void disallowReadLogs() {
+        mDefaultStorage.disallowReadLogs();
+    }
+
+    /**
+     * Resets the states and unbinds storage instances for an installation session.
+     */
+    public void cleanUpAndMarkComplete() {
+        IncrementalStorage defaultStorage = cleanUp();
+        if (defaultStorage != null) {
+            defaultStorage.onInstallationComplete();
+        }
+    }
+
+    private IncrementalStorage cleanUp() {
+        IncrementalStorage defaultStorage = mDefaultStorage;
+        mInheritedStorage = null;
+        mDefaultStorage = null;
+        if (defaultStorage == null) {
+            return null;
+        }
+
+        try {
+            mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath());
+            defaultStorage.unBind(mStageDir.getAbsolutePath());
+        } catch (IOException ignored) {
+        }
+        return defaultStorage;
+    }
+}
diff --git a/android-34/android/os/incremental/IncrementalManager.java b/android-34/android/os/incremental/IncrementalManager.java
new file mode 100644
index 0000000..8004143
--- /dev/null
+++ b/android-34/android/os/incremental/IncrementalManager.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.DataLoaderParams;
+import android.content.pm.IPackageLoadingProgressCallback;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Objects;
+
+/**
+ * Provides operations to open or create an IncrementalStorage, using IIncrementalService
+ * service. Example Usage:
+ *
+ * <blockquote><pre>
+ * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE);
+ * IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
+ * </pre></blockquote>
+ *
+ * @hide
+ */
+@SystemService(Context.INCREMENTAL_SERVICE)
+public final class IncrementalManager {
+    private static final String TAG = "IncrementalManager";
+
+    private static final String ALLOWED_PROPERTY = "incremental.allowed";
+
+    public static final int MIN_VERSION_TO_SUPPORT_FSVERITY = 2;
+
+    public static final int CREATE_MODE_TEMPORARY_BIND =
+            IIncrementalService.CREATE_MODE_TEMPORARY_BIND;
+    public static final int CREATE_MODE_PERMANENT_BIND =
+            IIncrementalService.CREATE_MODE_PERMANENT_BIND;
+    public static final int CREATE_MODE_CREATE =
+            IIncrementalService.CREATE_MODE_CREATE;
+    public static final int CREATE_MODE_OPEN_EXISTING =
+            IIncrementalService.CREATE_MODE_OPEN_EXISTING;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CREATE_MODE_"}, value = {
+            CREATE_MODE_TEMPORARY_BIND,
+            CREATE_MODE_PERMANENT_BIND,
+            CREATE_MODE_CREATE,
+            CREATE_MODE_OPEN_EXISTING,
+    })
+    public @interface CreateMode {
+    }
+
+    private final @Nullable IIncrementalService mService;
+
+    private final LoadingProgressCallbacks mLoadingProgressCallbacks =
+            new LoadingProgressCallbacks();
+
+    public IncrementalManager(IIncrementalService service) {
+        mService = service;
+    }
+
+    /**
+     * Opens or create an Incremental File System mounted directory and returns an
+     * IncrementalStorage object.
+     *
+     * @param path                Absolute path to mount Incremental File System on.
+     * @param params              IncrementalDataLoaderParams object to configure data loading.
+     * @param createMode          Mode for opening an old Incremental File System mount or creating
+     *                            a new mount.
+     * @return IncrementalStorage object corresponding to the mounted directory.
+     */
+    @Nullable
+    public IncrementalStorage createStorage(@NonNull String path,
+            @NonNull DataLoaderParams params,
+            @CreateMode int createMode) {
+        Objects.requireNonNull(path);
+        Objects.requireNonNull(params);
+        try {
+            final int id = mService.createStorage(path, params.getData(), createMode);
+            if (id < 0) {
+                return null;
+            }
+            return new IncrementalStorage(mService, id);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Opens an existing Incremental File System mounted directory and returns an IncrementalStorage
+     * object.
+     *
+     * @param path Absolute target path that Incremental File System has been mounted on.
+     * @return IncrementalStorage object corresponding to the mounted directory.
+     */
+    @Nullable
+    public IncrementalStorage openStorage(@NonNull String path) {
+        try {
+            final int id = mService.openStorage(path);
+            if (id < 0) {
+                return null;
+            }
+            final IncrementalStorage storage = new IncrementalStorage(mService, id);
+            return storage;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Opens or creates an IncrementalStorage that is linked to another IncrementalStorage.
+     *
+     * @return IncrementalStorage object corresponding to the linked storage.
+     */
+    @Nullable
+    public IncrementalStorage createStorage(@NonNull String path,
+            @NonNull IncrementalStorage linkedStorage, @CreateMode int createMode) {
+        int id = -1;
+        try {
+            // Incremental service mounts its newly created storage on top of the supplied path,
+            // ensure that the original mode remains the same after mounting.
+            StructStat st = Os.stat(path);
+            id = mService.createLinkedStorage(
+                    path, linkedStorage.getId(), createMode);
+            if (id < 0) {
+                return null;
+            }
+            Os.chmod(path, st.st_mode & 07777);
+            return new IncrementalStorage(mService, id);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ErrnoException e) {
+            if (id >= 0) {
+                try {
+                    mService.deleteStorage(id);
+                } catch (RemoteException re) {
+                    throw re.rethrowFromSystemServer();
+                }
+            }
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Link an app's files from the stage dir to the final installation location.
+     * The expected outcome of this method is:
+     * 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory
+     * of {@code afterCodeFile}.
+     * 2) All the files under {@code beforeCodeFile} will show up under {@code afterCodeFile}.
+     *
+     * @param beforeCodeFile Path that is currently bind-mounted and have APKs under it.
+     *                       Example: /data/app/vmdl*tmp
+     * @param afterCodeFile Path that should will have APKs after this method is called. Its parent
+     *                      directory should be bind-mounted to a directory under /data/incremental.
+     *                      Example: /data/app/~~[randomStringA]/[packageName]-[randomStringB]
+     * @throws IllegalArgumentException
+     * @throws IOException
+     */
+    public void linkCodePath(File beforeCodeFile, File afterCodeFile)
+            throws IllegalArgumentException, IOException {
+        final File beforeCodeAbsolute = beforeCodeFile.getAbsoluteFile();
+        final IncrementalStorage apkStorage = openStorage(beforeCodeAbsolute.toString());
+        if (apkStorage == null) {
+            throw new IllegalArgumentException("Not an Incremental path: " + beforeCodeAbsolute);
+        }
+        final String targetStorageDir = afterCodeFile.getAbsoluteFile().getParent();
+        final IncrementalStorage linkedApkStorage =
+                createStorage(targetStorageDir, apkStorage,
+                        IncrementalManager.CREATE_MODE_CREATE
+                                | IncrementalManager.CREATE_MODE_PERMANENT_BIND);
+        if (linkedApkStorage == null) {
+            throw new IOException("Failed to create linked storage at dir: " + targetStorageDir);
+        }
+        try {
+            final String afterCodePathName = afterCodeFile.getName();
+            linkFiles(apkStorage, beforeCodeAbsolute, "", linkedApkStorage, afterCodePathName);
+        } catch (Exception e) {
+            linkedApkStorage.unBind(targetStorageDir);
+            throw e;
+        }
+    }
+
+    /**
+     * Recursively set up directories and link all the files from source storage to target storage.
+     *
+     * @param sourceStorage The storage that has all the files and directories underneath.
+     * @param sourceAbsolutePath The absolute path of the directory that holds all files and dirs.
+     * @param sourceRelativePath The relative path on the source directory, e.g., "" or "lib".
+     * @param targetStorage The target storage that will have the same files and directories.
+     * @param targetRelativePath The relative path to the directory on the target storage that
+     *                           should have all the files and dirs underneath,
+     *                           e.g., "packageName-random".
+     * @throws IOException When makeDirectory or makeLink fails on the Incremental File System.
+     */
+    private void linkFiles(IncrementalStorage sourceStorage, File sourceAbsolutePath,
+            String sourceRelativePath, IncrementalStorage targetStorage,
+            String targetRelativePath) throws IOException {
+        final Path sourceBase = sourceAbsolutePath.toPath().resolve(sourceRelativePath);
+        final Path targetRelative = Paths.get(targetRelativePath);
+        Files.walkFileTree(sourceAbsolutePath.toPath(), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+                    throws IOException {
+                final Path relativeDir = sourceBase.relativize(dir);
+                targetStorage.makeDirectory(targetRelative.resolve(relativeDir).toString());
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                    throws IOException {
+                final Path relativeFile = sourceBase.relativize(file);
+                sourceStorage.makeLink(
+                        file.toAbsolutePath().toString(), targetStorage,
+                        targetRelative.resolve(relativeFile).toString());
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    /**
+     * Checks if Incremental feature is enabled on this device.
+     */
+    public static boolean isFeatureEnabled() {
+        return nativeIsEnabled();
+    }
+
+    /**
+     * 0 - IncFs is disabled.
+     * 1 - IncFs v1, core features, no PerUid support. Optional in R.
+     * 2 - IncFs v2, PerUid support, fs-verity support. Required in S.
+     */
+    public static int getVersion() {
+        return nativeIsEnabled() ? nativeIsV2Available() ? 2 : 1 : 0;
+    }
+
+    /**
+     * Checks if Incremental installations are allowed.
+     * A developer can disable Incremental installations by setting the property.
+     */
+    public static boolean isAllowed() {
+        return isFeatureEnabled() && android.os.SystemProperties.getBoolean(ALLOWED_PROPERTY, true);
+    }
+
+    /**
+     * Checks if path is mounted on Incremental File System.
+     */
+    public static boolean isIncrementalPath(@NonNull String path) {
+        return nativeIsIncrementalPath(path);
+    }
+
+    /**
+     * Checks if an fd corresponds to a file on a mounted Incremental File System.
+     */
+    public static boolean isIncrementalFileFd(@NonNull FileDescriptor fd) {
+        return nativeIsIncrementalFd(fd.getInt$());
+    }
+
+    /**
+     * Returns raw signature for file if it's on Incremental File System.
+     * Unsafe, use only if you are sure what you are doing.
+     */
+    public static @Nullable byte[] unsafeGetFileSignature(@NonNull String path) {
+        return nativeUnsafeGetFileSignature(path);
+    }
+
+    /**
+     * Closes a storage specified by the absolute path. If the path is not Incremental, do nothing.
+     * Unbinds the target dir and deletes the corresponding storage instance.
+     * Deletes the package name and associated storage id from maps.
+     */
+    public void rmPackageDir(@NonNull File codeFile) {
+        try {
+            final String codePath = codeFile.getAbsolutePath();
+            final IncrementalStorage storage = openStorage(codePath);
+            if (storage == null) {
+                return;
+            }
+            mLoadingProgressCallbacks.cleanUpCallbacks(storage);
+            storage.unBind(codePath);
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to remove code path", e);
+        }
+    }
+
+    /**
+     * Called when a new callback wants to listen to the loading progress of an installed package.
+     * Increment the count of callbacks associated to the corresponding storage.
+     * Only register storage listener if there hasn't been any existing callback on the storage yet.
+     * @param codePath Path of the installed package. This path is on an Incremental Storage.
+     * @param callback To report loading progress to.
+     * @return True if the package name and associated storage id are valid. False otherwise.
+     */
+    public boolean registerLoadingProgressCallback(@NonNull String codePath,
+            @NonNull IPackageLoadingProgressCallback callback) {
+        final IncrementalStorage storage = openStorage(codePath);
+        if (storage == null) {
+            // storage does not exist, package not installed
+            return false;
+        }
+        return mLoadingProgressCallbacks.registerCallback(storage, callback);
+    }
+
+    /**
+     * Called to stop all listeners from listening to loading progress of an installed package.
+     * @param codePath Path of the installed package
+     */
+    public void unregisterLoadingProgressCallbacks(@NonNull String codePath) {
+        final IncrementalStorage storage = openStorage(codePath);
+        if (storage == null) {
+            // storage does not exist, package not installed
+            return;
+        }
+        mLoadingProgressCallbacks.cleanUpCallbacks(storage);
+    }
+
+    private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub {
+        @GuardedBy("mCallbacks")
+        private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks =
+                new SparseArray<>();
+
+        public void cleanUpCallbacks(@NonNull IncrementalStorage storage) {
+            final int storageId = storage.getId();
+            final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
+            synchronized (mCallbacks) {
+                callbacksForStorage = mCallbacks.removeReturnOld(storageId);
+            }
+            if (callbacksForStorage == null) {
+                return;
+            }
+            // Unregister all existing callbacks on this storage
+            callbacksForStorage.kill();
+            storage.unregisterLoadingProgressListener();
+        }
+
+        public boolean registerCallback(@NonNull IncrementalStorage storage,
+                @NonNull IPackageLoadingProgressCallback callback) {
+            final int storageId = storage.getId();
+            synchronized (mCallbacks) {
+                RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage =
+                        mCallbacks.get(storageId);
+                if (callbacksForStorage == null) {
+                    callbacksForStorage = new RemoteCallbackList<>();
+                    mCallbacks.put(storageId, callbacksForStorage);
+                }
+                // Registration in RemoteCallbackList needs to be done first, such that when events
+                // come from Incremental Service, the callback is already registered
+                callbacksForStorage.register(callback);
+                if (callbacksForStorage.getRegisteredCallbackCount() > 1) {
+                    // already listening for progress for this storage
+                    return true;
+                }
+            }
+            return storage.registerLoadingProgressListener(this);
+        }
+
+        @Override
+        public void onStorageLoadingProgressChanged(int storageId, float progress) {
+            final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
+            synchronized (mCallbacks) {
+                callbacksForStorage = mCallbacks.get(storageId);
+            }
+            if (callbacksForStorage == null) {
+                // no callback has ever been registered on this storage
+                return;
+            }
+            final int n = callbacksForStorage.beginBroadcast();
+            // RemoteCallbackList use ArrayMap internally and it's safe to iterate this way
+            for (int i = 0; i < n; i++) {
+                final IPackageLoadingProgressCallback callback =
+                        callbacksForStorage.getBroadcastItem(i);
+                try {
+                    callback.onPackageLoadingProgressChanged(progress);
+                } catch (RemoteException ignored) {
+                }
+            }
+            callbacksForStorage.finishBroadcast();
+        }
+    }
+
+    /**
+     * Returns the metrics of an Incremental Storage.
+     */
+    public IncrementalMetrics getMetrics(@NonNull String codePath) {
+        final IncrementalStorage storage = openStorage(codePath);
+        if (storage == null) {
+            // storage does not exist, package not installed
+            return null;
+        }
+        return new IncrementalMetrics(storage.getMetrics());
+    }
+
+    /* Native methods */
+    private static native boolean nativeIsEnabled();
+    private static native boolean nativeIsV2Available();
+    private static native boolean nativeIsIncrementalPath(@NonNull String path);
+    private static native boolean nativeIsIncrementalFd(@NonNull int fd);
+    private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
+}
diff --git a/android-34/android/os/incremental/IncrementalMetrics.java b/android-34/android/os/incremental/IncrementalMetrics.java
new file mode 100644
index 0000000..534525a
--- /dev/null
+++ b/android-34/android/os/incremental/IncrementalMetrics.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 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.os.incremental;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+/**
+ * Provides methods to access metrics about an app installed via Incremental
+ * @hide
+ */
+public class IncrementalMetrics {
+    @NonNull private final PersistableBundle mData;
+
+    public IncrementalMetrics(@NonNull PersistableBundle data) {
+        mData = data;
+    }
+
+    /**
+     * @return Milliseconds between now and when the oldest pending read happened
+     */
+    public long getMillisSinceOldestPendingRead() {
+        return mData.getLong(IIncrementalService.METRICS_MILLIS_SINCE_OLDEST_PENDING_READ, -1);
+    }
+
+    /**
+     * @return Whether read logs are enabled
+     */
+    public boolean getReadLogsEnabled() {
+        return mData.getBoolean(IIncrementalService.METRICS_READ_LOGS_ENABLED, false);
+    }
+
+    /**
+     * @return storage health status code. @see android.os.incremental.IStorageHealthListener
+     */
+    public int getStorageHealthStatusCode() {
+        return mData.getInt(IIncrementalService.METRICS_STORAGE_HEALTH_STATUS_CODE, -1);
+    }
+
+    /**
+     * @return data loader status code. @see android.content.pm.IDataLoaderStatusListener
+     */
+    public int getDataLoaderStatusCode() {
+        return mData.getInt(IIncrementalService.METRICS_DATA_LOADER_STATUS_CODE, -1);
+    }
+
+    /**
+     * @return duration since last data loader binding attempt
+     */
+    public long getMillisSinceLastDataLoaderBind() {
+        return mData.getLong(IIncrementalService.METRICS_MILLIS_SINCE_LAST_DATA_LOADER_BIND, -1);
+    }
+
+    /**
+     * @return delay in milliseconds to retry data loader binding
+     */
+    public long getDataLoaderBindDelayMillis() {
+        return mData.getLong(IIncrementalService.METRICS_DATA_LOADER_BIND_DELAY_MILLIS, -1);
+    }
+
+    /**
+     * @return total count of delayed reads caused by pending reads
+     */
+    public int getTotalDelayedReads() {
+        return mData.getInt(IIncrementalService.METRICS_TOTAL_DELAYED_READS, -1);
+    }
+
+    /**
+     * @return total count of failed reads
+     */
+    public int getTotalFailedReads() {
+        return mData.getInt(IIncrementalService.METRICS_TOTAL_FAILED_READS, -1);
+    }
+
+    /**
+     * @return total duration in milliseconds of delayed reads
+     */
+    public long getTotalDelayedReadsDurationMillis() {
+        return mData.getLong(IIncrementalService.METRICS_TOTAL_DELAYED_READS_MILLIS, -1);
+    }
+
+    /**
+     * @return the uid of the last read error
+     */
+    public int getLastReadErrorUid() {
+        return mData.getInt(IIncrementalService.METRICS_LAST_READ_ERROR_UID, -1);
+    }
+
+    /**
+     * @return duration in milliseconds since the last read error
+     */
+    public long getMillisSinceLastReadError() {
+        return mData.getLong(IIncrementalService.METRICS_MILLIS_SINCE_LAST_READ_ERROR, -1);
+    }
+
+    /**
+     * @return the error number of the last read error
+     */
+    public int getLastReadErrorNumber() {
+        return mData.getInt(IIncrementalService.METRICS_LAST_READ_ERROR_NUMBER, -1);
+    }
+}
diff --git a/android-34/android/os/incremental/IncrementalStorage.java b/android-34/android/os/incremental/IncrementalStorage.java
new file mode 100644
index 0000000..9138409
--- /dev/null
+++ b/android-34/android/os/incremental/IncrementalStorage.java
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Provides operations on an Incremental File System directory, using IncrementalServiceNative.
+ * Example usage:
+ *
+ * <blockquote><pre>
+ * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE);
+ * IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
+ * storage.makeDirectory("subdir");
+ * </pre></blockquote>
+ *
+ * @hide
+ */
+public final class IncrementalStorage {
+    private static final String TAG = "IncrementalStorage";
+    private final int mId;
+    private final IIncrementalService mService;
+
+
+    public IncrementalStorage(@NonNull IIncrementalService is, int id) {
+        mService = is;
+        mId = id;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Temporarily bind-mounts the current storage directory to a target directory. The bind-mount
+     * will NOT be preserved between device reboots.
+     *
+     * @param targetPath Absolute path to the target directory.
+     */
+    public void bind(@NonNull String targetPath) throws IOException {
+        bind("", targetPath);
+    }
+
+    /**
+     * Temporarily bind-mounts a subdir under the current storage directory to a target directory.
+     * The bind-mount will NOT be preserved between device reboots.
+     *
+     * @param sourcePath Source path as a relative path under current storage
+     *                   directory.
+     * @param targetPath Absolute path to the target directory.
+     */
+    public void bind(@NonNull String sourcePath, @NonNull String targetPath)
+            throws IOException {
+        try {
+            int res = mService.makeBindMount(mId, sourcePath, targetPath,
+                    IIncrementalService.BIND_TEMPORARY);
+            if (res < 0) {
+                throw new IOException("bind() failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Permanently bind-mounts the current storage directory to a target directory. The bind-mount
+     * WILL be preserved between device reboots.
+     *
+     * @param targetPath Absolute path to the target directory.
+     */
+    public void bindPermanent(@NonNull String targetPath) throws IOException {
+        bindPermanent("", targetPath);
+    }
+
+    /**
+     * Permanently bind-mounts a subdir under the current storage directory to a target directory.
+     * The bind-mount WILL be preserved between device reboots.
+     *
+     * @param sourcePath Relative path under the current storage directory.
+     * @param targetPath Absolute path to the target directory.
+     */
+    public void bindPermanent(@NonNull String sourcePath, @NonNull String targetPath)
+            throws IOException {
+        try {
+            int res = mService.makeBindMount(mId, sourcePath, targetPath,
+                    IIncrementalService.BIND_PERMANENT);
+            if (res < 0) {
+                throw new IOException("bind() permanent failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unbinds a bind mount.
+     *
+     * @param targetPath Absolute path to the target directory.
+     */
+    public void unBind(@NonNull String targetPath) throws IOException {
+        try {
+            int res = mService.deleteBindMount(mId, targetPath);
+            if (res < 0) {
+                throw new IOException("unbind() failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a sub-directory under the current storage directory.
+     *
+     * @param path Relative path of the sub-directory, e.g., "subdir"
+     */
+    public void makeDirectory(@NonNull String path) throws IOException {
+        try {
+            int res = mService.makeDirectory(mId, path);
+            if (res < 0) {
+                throw new IOException("makeDirectory() failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a sub-directory under the current storage directory. If its parent dirs do not exist,
+     * create the parent dirs as well.
+     *
+     * @param path Full path.
+     */
+    public void makeDirectories(@NonNull String path) throws IOException {
+        try {
+            int res = mService.makeDirectories(mId, path);
+            if (res < 0) {
+                throw new IOException("makeDirectory() failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a file under the current storage directory.
+     *
+     * @param path             Relative path of the new file.
+     * @param size             Size of the new file in bytes.
+     * @param mode             File access permission mode.
+     * @param metadata         Metadata bytes.
+     * @param v4signatureBytes Serialized V4SignatureProto.
+     * @param content          Optionally set file content.
+     */
+    public void makeFile(@NonNull String path, long size, int mode, @Nullable UUID id,
+            @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes, @Nullable byte[] content)
+            throws IOException {
+        try {
+            if (id == null && metadata == null) {
+                throw new IOException("File ID and metadata cannot both be null");
+            }
+            validateV4Signature(v4signatureBytes);
+            final IncrementalNewFileParams params = new IncrementalNewFileParams();
+            params.size = size;
+            params.metadata = (metadata == null ? new byte[0] : metadata);
+            params.fileId = idToBytes(id);
+            params.signature = v4signatureBytes;
+            int res = mService.makeFile(mId, path, mode, params, content);
+            if (res != 0) {
+                throw new IOException("makeFile() failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a file in Incremental storage. The content of the file is mapped from a range inside
+     * a source file in the same storage.
+     *
+     * @param destPath           Target full path.
+     * @param sourcePath         Source full path.
+     * @param rangeStart         Starting offset (in bytes) in the source file.
+     * @param rangeEnd           Ending offset (in bytes) in the source file.
+     */
+    public void makeFileFromRange(@NonNull String destPath,
+            @NonNull String sourcePath, long rangeStart, long rangeEnd) throws IOException {
+        try {
+            int res = mService.makeFileFromRange(mId, destPath, sourcePath,
+                    rangeStart, rangeEnd);
+            if (res < 0) {
+                throw new IOException("makeFileFromRange() failed, errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a hard-link between two paths, which can be under different storages but in the same
+     * Incremental File System.
+     *
+     * @param sourcePath    The absolute path of the source.
+     * @param destStorage   The target storage of the link target.
+     * @param destPath      The absolute path of the target.
+     */
+    public void makeLink(@NonNull String sourcePath, IncrementalStorage destStorage,
+            @NonNull String destPath) throws IOException {
+        try {
+            int res = mService.makeLink(mId, sourcePath, destStorage.getId(),
+                    destPath);
+            if (res < 0) {
+                throw new IOException("makeLink() failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Deletes a hard-link under the current storage directory.
+     *
+     * @param path The absolute path of the target.
+     */
+    public void unlink(@NonNull String path) throws IOException {
+        try {
+            int res = mService.unlink(mId, path);
+            if (res < 0) {
+                throw new IOException("unlink() failed with errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Rename an old file name to a new file name under the current storage directory.
+     *
+     * @param sourcepath Old file path as a full path to the storage directory.
+     * @param destpath   New file path as a full path to the storage directory.
+     */
+    public void moveFile(@NonNull String sourcepath,
+            @NonNull String destpath) throws IOException {
+        //TODO(zyy): implement using rename(2) when confirmed that IncFS supports it.
+        try {
+            int res = mService.makeLink(mId, sourcepath, mId, destpath);
+            if (res < 0) {
+                throw new IOException("moveFile() failed at makeLink(), errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        try {
+            mService.unlink(mId, sourcepath);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
+     * Move a directory, which is bind-mounted to a given storage, to a new location. The bind mount
+     * will be persistent between reboots.
+     *
+     * @param sourcePath The old path of the directory as an absolute path.
+     * @param destPath   The new path of the directory as an absolute path, expected to already
+     *                   exist.
+     */
+    public void moveDir(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
+        if (!new File(destPath).exists()) {
+            throw new IOException("moveDir() requires that destination dir already exists.");
+        }
+        try {
+            int res = mService.makeBindMount(mId, sourcePath, destPath,
+                    IIncrementalService.BIND_PERMANENT);
+            if (res < 0) {
+                throw new IOException("moveDir() failed at making bind mount, errno " + -res);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        try {
+            mService.deleteBindMount(mId, sourcePath);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
+     * Checks whether a file under the current storage directory is fully loaded.
+     *
+     * @param path The relative path of the file.
+     * @return True if the file is fully loaded.
+     */
+    public boolean isFileFullyLoaded(@NonNull String path) throws IOException {
+        try {
+            int res = mService.isFileFullyLoaded(mId, path);
+            if (res < 0) {
+                throw new IOException("isFileFullyLoaded() failed, errno " + -res);
+            }
+            return res == 0;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+
+    /**
+     * Checks if all files in the storage are fully loaded.
+     */
+    public boolean isFullyLoaded() throws IOException {
+        try {
+            final int res = mService.isFullyLoaded(mId);
+            if (res < 0) {
+                throw new IOException(
+                        "isFullyLoaded() failed at querying loading progress, errno " + -res);
+            }
+            return res == 0;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
+     * Returns the loading progress of a storage
+     *
+     * @return progress value between [0, 1].
+     */
+    public float getLoadingProgress() throws IOException {
+        try {
+            final float res = mService.getLoadingProgress(mId);
+            if (res < 0) {
+                throw new IOException(
+                        "getLoadingProgress() failed at querying loading progress, errno " + -res);
+            }
+            return res;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the metadata object of an IncFs File.
+     *
+     * @param path The relative path of the file.
+     * @return Byte array that contains metadata bytes.
+     */
+    @Nullable
+    public byte[] getFileMetadata(@NonNull String path) {
+        try {
+            return mService.getMetadataByPath(mId, path);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Returns the metadata object of an IncFs File.
+     *
+     * @param id The file id.
+     * @return Byte array that contains metadata bytes.
+     */
+    @Nullable
+    public byte[] getFileMetadata(@NonNull UUID id) {
+        try {
+            final byte[] rawId = idToBytes(id);
+            return mService.getMetadataById(mId, rawId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Initializes and starts the DataLoader.
+     * This makes sure all install-time parameters are applied.
+     * Does not affect persistent DataLoader params.
+     * @return True if start request was successfully queued.
+     */
+    public boolean startLoading(
+            @NonNull DataLoaderParams dataLoaderParams,
+            @Nullable IDataLoaderStatusListener statusListener,
+            @Nullable StorageHealthCheckParams healthCheckParams,
+            @Nullable IStorageHealthListener healthListener,
+            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
+        Objects.requireNonNull(perUidReadTimeouts);
+        try {
+            return mService.startLoading(mId, dataLoaderParams.getData(), statusListener,
+                    healthCheckParams, healthListener, perUidReadTimeouts);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
+     * Marks the completion of installation.
+     */
+    public void onInstallationComplete() {
+        try {
+            mService.onInstallationComplete(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+
+    private static final int UUID_BYTE_SIZE = 16;
+
+    /**
+     * Converts UUID to a byte array usable for Incremental API calls
+     *
+     * @param id The id to convert
+     * @return Byte array that contains the same ID.
+     */
+    @NonNull
+    public static byte[] idToBytes(@Nullable UUID id) {
+        if (id == null) {
+            return new byte[0];
+        }
+        final ByteBuffer buf = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+        buf.putLong(id.getMostSignificantBits());
+        buf.putLong(id.getLeastSignificantBits());
+        return buf.array();
+    }
+
+    /**
+     * Converts UUID from a byte array usable for Incremental API calls
+     *
+     * @param bytes The id in byte array format, 16 bytes long
+     * @return UUID constructed from the byte array.
+     */
+    @NonNull
+    public static UUID bytesToId(byte[] bytes) throws IllegalArgumentException {
+        if (bytes.length != UUID_BYTE_SIZE) {
+            throw new IllegalArgumentException("Expected array of size " + UUID_BYTE_SIZE
+                                               + ", got " + bytes.length);
+        }
+        final ByteBuffer buf = ByteBuffer.wrap(bytes);
+        long msb = buf.getLong();
+        long lsb = buf.getLong();
+        return new UUID(msb, lsb);
+    }
+
+    private static final int INCFS_MAX_HASH_SIZE = 32; // SHA256
+    private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
+
+    /**
+     * Permanently disable readlogs collection.
+     */
+    public void disallowReadLogs() {
+        try {
+            mService.disallowReadLogs(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Deserialize and validate v4 signature bytes.
+     */
+    private static void validateV4Signature(@Nullable byte[] v4signatureBytes)
+            throws IOException {
+        if (v4signatureBytes == null || v4signatureBytes.length == 0) {
+            return;
+        }
+
+        final V4Signature signature;
+        try {
+            signature = V4Signature.readFrom(v4signatureBytes);
+        } catch (IOException e) {
+            throw new IOException("Failed to read v4 signature:", e);
+        }
+
+        if (!signature.isVersionSupported()) {
+            throw new IOException("v4 signature version " + signature.version
+                    + " is not supported");
+        }
+
+        final V4Signature.HashingInfo hashingInfo = V4Signature.HashingInfo.fromByteArray(
+                signature.hashingInfo);
+        final V4Signature.SigningInfos signingInfos = V4Signature.SigningInfos.fromByteArray(
+                signature.signingInfos);
+
+        if (hashingInfo.hashAlgorithm != V4Signature.HASHING_ALGORITHM_SHA256) {
+            throw new IOException("Unsupported hashAlgorithm: " + hashingInfo.hashAlgorithm);
+        }
+        if (hashingInfo.log2BlockSize != V4Signature.LOG2_BLOCK_SIZE_4096_BYTES) {
+            throw new IOException("Unsupported log2BlockSize: " + hashingInfo.log2BlockSize);
+        }
+        if (hashingInfo.salt != null && hashingInfo.salt.length > 0) {
+            throw new IOException("Unsupported salt: " + Arrays.toString(hashingInfo.salt));
+        }
+        if (hashingInfo.rawRootHash.length != INCFS_MAX_HASH_SIZE) {
+            throw new IOException("rawRootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
+        }
+        if (signingInfos.signingInfo.additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
+            throw new IOException(
+                    "additionalData has to be at most " + INCFS_MAX_ADD_DATA_SIZE + " bytes");
+        }
+    }
+
+    /**
+     * Configure all the lib files inside Incremental Service, e.g., create lib dirs, create new lib
+     * files, extract original lib file data from zip and then write data to the lib files on the
+     * Incremental File System.
+     *
+     * @param apkFullPath Source APK to extract native libs from.
+     * @param libDirRelativePath Target dir to put lib files, e.g., "lib" or "lib/arm".
+     * @param abi Target ABI of the native lib files. Only extract native libs of this ABI.
+     * @param extractNativeLibs If true, extract native libraries; otherwise just setup directories
+     *                          without extracting.
+     * @return Success of not.
+     */
+    public boolean configureNativeBinaries(String apkFullPath, String libDirRelativePath,
+            String abi, boolean extractNativeLibs) {
+        try {
+            return mService.configureNativeBinaries(mId, apkFullPath, libDirRelativePath, abi,
+                    extractNativeLibs);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
+     * Waits for all native binary extraction operations to complete on the storage.
+     *
+     * @return Success of not.
+     */
+    public boolean waitForNativeBinariesExtraction() {
+        try {
+            return mService.waitForNativeBinariesExtraction(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
+     * Register to listen to loading progress of all the files on this storage.
+     * @param listener To report progress from Incremental Service to the caller.
+     */
+    public boolean registerLoadingProgressListener(IStorageLoadingProgressListener listener) {
+        try {
+            return mService.registerLoadingProgressListener(mId, listener);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
+     * Unregister to stop listening to storage loading progress.
+     */
+    public boolean unregisterLoadingProgressListener() {
+        try {
+            return mService.unregisterLoadingProgressListener(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
+     * Returns the metrics of the current storage.
+     * {@see IIncrementalService} for metrics keys.
+     */
+    public PersistableBundle getMetrics() {
+        try {
+            return mService.getMetrics(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+}
diff --git a/android-34/android/os/incremental/V4Signature.java b/android-34/android/os/incremental/V4Signature.java
new file mode 100644
index 0000000..2044502
--- /dev/null
+++ b/android-34/android/os/incremental/V4Signature.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2020 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.os.incremental;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ParcelFileDescriptor;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+
+/**
+ * V4 signature fields.
+ * Keep in sync with APKSig authoritative copy.
+ * @hide
+ */
+public class V4Signature {
+    public static final String EXT = ".idsig";
+    public static final int SUPPORTED_VERSION = 2;
+
+    public static final int HASHING_ALGORITHM_SHA256 = 1;
+    public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;
+
+    public static final int INCFS_MAX_SIGNATURE_SIZE = 8096;  // incrementalfs.h
+
+    /**
+     * IncFS hashing data.
+     */
+    public static class HashingInfo {
+        public final int hashAlgorithm; // only 1 == SHA256 supported
+        public final byte log2BlockSize; // only 12 (block size 4096) supported now
+        @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
+        @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page
+
+        HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
+            this.hashAlgorithm = hashAlgorithm;
+            this.log2BlockSize = log2BlockSize;
+            this.salt = salt;
+            this.rawRootHash = rawRootHash;
+        }
+
+        /**
+         * Constructs HashingInfo from byte array.
+         */
+        @NonNull
+        public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException {
+            ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+            final int hashAlgorithm = buffer.getInt();
+            final byte log2BlockSize = buffer.get();
+            byte[] salt = readBytes(buffer);
+            byte[] rawRootHash = readBytes(buffer);
+            return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash);
+        }
+    }
+
+    /**
+     * Signature data.
+     */
+    public static class SigningInfo {
+        public final byte[] apkDigest;  // used to match with the corresponding APK
+        public final byte[] certificate; // ASN.1 DER form
+        public final byte[] additionalData; // a free-form binary data blob
+        public final byte[] publicKey; // ASN.1 DER, must match the certificate
+        public final int signatureAlgorithmId; // see the APK v2 doc for the list
+        public final byte[] signature;
+
+        SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
+                byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
+            this.apkDigest = apkDigest;
+            this.certificate = certificate;
+            this.additionalData = additionalData;
+            this.publicKey = publicKey;
+            this.signatureAlgorithmId = signatureAlgorithmId;
+            this.signature = signature;
+        }
+
+        /**
+         * Constructs SigningInfo from byte array.
+         */
+        public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
+            return fromByteBuffer(ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN));
+        }
+
+        /**
+         * Constructs SigningInfo from byte buffer.
+         */
+        public static SigningInfo fromByteBuffer(ByteBuffer buffer) throws IOException {
+            byte[] apkDigest = readBytes(buffer);
+            byte[] certificate = readBytes(buffer);
+            byte[] additionalData = readBytes(buffer);
+            byte[] publicKey = readBytes(buffer);
+            int signatureAlgorithmId = buffer.getInt();
+            byte[] signature = readBytes(buffer);
+            return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
+                    signatureAlgorithmId, signature);
+        }
+    }
+
+    /**
+     * Optional signature data block with ID.
+     */
+    public static class SigningInfoBlock {
+        public final int blockId;
+        public final byte[] signingInfo;
+
+        public SigningInfoBlock(int blockId, byte[] signingInfo) {
+            this.blockId = blockId;
+            this.signingInfo = signingInfo;
+        }
+
+        static SigningInfoBlock fromByteBuffer(ByteBuffer buffer) throws IOException {
+            int blockId = buffer.getInt();
+            byte[] signingInfo = readBytes(buffer);
+            return new SigningInfoBlock(blockId, signingInfo);
+        }
+    }
+
+    /**
+     * V4 signature data.
+     */
+    public static class SigningInfos {
+        // Default signature.
+        public final SigningInfo signingInfo;
+        // Additional signatures corresponding to extended V2/V3/V31 blocks.
+        public final SigningInfoBlock[] signingInfoBlocks;
+
+        public SigningInfos(SigningInfo signingInfo) {
+            this.signingInfo = signingInfo;
+            this.signingInfoBlocks = new SigningInfoBlock[0];
+        }
+
+        public SigningInfos(SigningInfo signingInfo, SigningInfoBlock... signingInfoBlocks) {
+            this.signingInfo = signingInfo;
+            this.signingInfoBlocks = signingInfoBlocks;
+        }
+
+        /**
+         * Constructs SigningInfos from byte array.
+         */
+        public static SigningInfos fromByteArray(byte[] bytes) throws IOException {
+            ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+            SigningInfo signingInfo = SigningInfo.fromByteBuffer(buffer);
+            if (!buffer.hasRemaining()) {
+                return new SigningInfos(signingInfo);
+            }
+            ArrayList<SigningInfoBlock> signingInfoBlocks = new ArrayList<>(1);
+            while (buffer.hasRemaining()) {
+                signingInfoBlocks.add(SigningInfoBlock.fromByteBuffer(buffer));
+            }
+            return new SigningInfos(signingInfo,
+                    signingInfoBlocks.toArray(new SigningInfoBlock[signingInfoBlocks.size()]));
+        }
+    }
+
+    public final int version; // Always 2 for now.
+    /**
+     * Raw byte array containing the IncFS hashing data.
+     * @see HashingInfo#fromByteArray(byte[])
+     */
+    @Nullable public final byte[] hashingInfo;
+
+    /**
+     * Raw byte array containing V4 signatures.
+     * <p>Passed as-is to the kernel. Can be retrieved later.
+     * @see SigningInfos#fromByteArray(byte[])
+     */
+    @Nullable public final byte[] signingInfos;
+
+    /**
+     * Construct a V4Signature from .idsig file.
+     */
+    public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException {
+        try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) {
+            return readFrom(stream);
+        }
+    }
+
+    /**
+     * Construct a V4Signature from a byte array.
+     */
+    @NonNull
+    public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException {
+        try (InputStream stream = new ByteArrayInputStream(bytes)) {
+            return readFrom(stream);
+        }
+    }
+
+    /**
+     * Store the V4Signature to a byte-array.
+     */
+    public byte[] toByteArray() {
+        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+            this.writeTo(stream);
+            return stream.toByteArray();
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Combines necessary data to a signed data blob.
+     * The blob can be validated against signingInfo.signature.
+     *
+     * @param fileSize - size of the signed file (APK)
+     */
+    public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo,
+            SigningInfo signingInfo) {
+        final int size =
+                4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
+                        hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
+                        signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
+                        signingInfo.additionalData);
+        ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
+        buffer.putInt(size);
+        buffer.putLong(fileSize);
+        buffer.putInt(hashingInfo.hashAlgorithm);
+        buffer.put(hashingInfo.log2BlockSize);
+        writeBytes(buffer, hashingInfo.salt);
+        writeBytes(buffer, hashingInfo.rawRootHash);
+        writeBytes(buffer, signingInfo.apkDigest);
+        writeBytes(buffer, signingInfo.certificate);
+        writeBytes(buffer, signingInfo.additionalData);
+        return buffer.array();
+    }
+
+    public boolean isVersionSupported() {
+        return this.version == SUPPORTED_VERSION;
+    }
+
+    private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfos) {
+        this.version = version;
+        this.hashingInfo = hashingInfo;
+        this.signingInfos = signingInfos;
+    }
+
+    private static V4Signature readFrom(InputStream stream) throws IOException {
+        final int version = readIntLE(stream);
+        int maxSize = INCFS_MAX_SIGNATURE_SIZE;
+        final byte[] hashingInfo = readBytes(stream, maxSize);
+        if (hashingInfo != null) {
+            maxSize -= hashingInfo.length;
+        }
+        final byte[] signingInfo = readBytes(stream, maxSize);
+        return new V4Signature(version, hashingInfo, signingInfo);
+    }
+
+    private void writeTo(OutputStream stream) throws IOException {
+        writeIntLE(stream, this.version);
+        writeBytes(stream, this.hashingInfo);
+        writeBytes(stream, this.signingInfos);
+    }
+
+    // Utility methods.
+    private static int bytesSize(byte[] bytes) {
+        return 4/*length*/ + (bytes == null ? 0 : bytes.length);
+    }
+
+    private static void readFully(InputStream stream, byte[] buffer) throws IOException {
+        int len = buffer.length;
+        int n = 0;
+        while (n < len) {
+            int count = stream.read(buffer, n, len - n);
+            if (count < 0) {
+                throw new EOFException();
+            }
+            n += count;
+        }
+    }
+
+    private static int readIntLE(InputStream stream) throws IOException {
+        final byte[] buffer = new byte[4];
+        readFully(stream, buffer);
+        return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
+    }
+
+    private static void writeIntLE(OutputStream stream, int v) throws IOException {
+        final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt(
+                v).array();
+        stream.write(buffer);
+    }
+
+    private static byte[] readBytes(InputStream stream, int maxSize) throws IOException {
+        try {
+            final int size = readIntLE(stream);
+            if (size > maxSize) {
+                throw new IOException(
+                        "Signature is too long. Max allowed is " + INCFS_MAX_SIGNATURE_SIZE);
+            }
+            final byte[] bytes = new byte[size];
+            readFully(stream, bytes);
+            return bytes;
+        } catch (EOFException ignored) {
+            return null;
+        }
+    }
+
+    private static byte[] readBytes(ByteBuffer buffer) throws IOException {
+        if (buffer.remaining() < 4) {
+            throw new EOFException();
+        }
+        final int size = buffer.getInt();
+        if (buffer.remaining() < size) {
+            throw new EOFException();
+        }
+        final byte[] bytes = new byte[size];
+        buffer.get(bytes);
+        return bytes;
+    }
+
+    private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException {
+        if (bytes == null) {
+            writeIntLE(stream, 0);
+            return;
+        }
+        writeIntLE(stream, bytes.length);
+        stream.write(bytes);
+    }
+
+    private static void writeBytes(ByteBuffer buffer, byte[] bytes) {
+        if (bytes == null) {
+            buffer.putInt(0);
+            return;
+        }
+        buffer.putInt(bytes.length);
+        buffer.put(bytes);
+    }
+}
diff --git a/android-34/android/os/storage/CrateInfo.java b/android-34/android/os/storage/CrateInfo.java
new file mode 100644
index 0000000..418d39e
--- /dev/null
+++ b/android-34/android/os/storage/CrateInfo.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.UUID;
+
+/**
+ * The CrateInfo describe the crate information.
+ * <p>
+ *      It describe the following items.
+ *      <ul>
+ *          <li>The crate id that is the name of the child directory in
+ *          {@link Context#getCrateDir(String)}</li>
+ *          <li>Label to provide human readable text for the users.</li>
+ *          <li>Expiration information. When the crate is expired and the run .</li>
+ *
+ *      </ul>for the directory
+ * </p>
+ * @hide
+ */
+@TestApi
+public final class CrateInfo implements Parcelable {
+    private static final String TAG = "CrateInfo";
+
+    /**
+     * The following member fields whose value are set by apps and retrieved by system_server.
+     */
+    private CharSequence mLabel;
+    @CurrentTimeMillisLong
+    private long mExpiration;
+
+    /**
+     * The following member fields whose value are retrieved by installd.
+     * <p>{@link android.app.usage.StorageStatsManager#queryCratesForUser(UUID, UserHandle)} query
+     * all of crates for the specified UserHandle. That means the return crate list whose elements
+     * may have the same userId but different package name. Each crate needs the information to tell
+     * the caller from where package comes.
+     * </p>
+     */
+    private int mUid;
+
+    /**
+     * The following member fields whose value are retrieved by installd.
+     * <p>Both {@link StorageStatsManager#queryCratesForUid(UUID, int)} and
+     * {@link android.app.usage.StorageStatsManager#queryCratesForUser(UUID, UserHandle)} query
+     * all of crates for the specified uid or userId. That means the return crate list whose
+     * elements may have the same uid or userId but different package name. Each crate needs the
+     * information to tell the caller from where package comes.
+     * </p>
+     */
+    @Nullable
+    private String mPackageName;
+
+    /**
+     * The following member fields whose value are retrieved by system_server.
+     * <p>
+     *     The child directories in {@link Context#getCrateDir(String)} are crates. Each directories
+     *     is a crate. The folder name is the crate id.
+     * </p><p>
+     *     Can't apply check if the path is validated or not because it need pass through the
+     *     parcel.
+     * </p>
+     */
+    @Nullable
+    private String mId;
+
+    private CrateInfo() {
+        mExpiration = 0;
+    }
+
+    /**
+     * To create the crateInfo by passing validated label.
+     * @param label a display name for the crate
+     * @param expiration It's positive integer. if current time is larger than the expiration, the
+     *                  files under this crate will be considered to be deleted. Default value is 0.
+     * @throws IllegalArgumentException cause IllegalArgumentException when label is null
+     *      or empty string
+     */
+    public CrateInfo(@NonNull CharSequence label, @CurrentTimeMillisLong long expiration) {
+        Preconditions.checkStringNotEmpty(label,
+                "Label should not be either null or empty string");
+        Preconditions.checkArgumentNonnegative(expiration,
+                "Expiration should be non negative number");
+
+        mLabel = label;
+        mExpiration = expiration;
+    }
+
+    /**
+     * To create the crateInfo by passing validated label.
+     * @param label a display name for the crate
+     * @throws IllegalArgumentException cause IllegalArgumentException when label is null
+     *      or empty string
+     */
+    public CrateInfo(@NonNull CharSequence label) {
+        this(label, 0);
+    }
+
+    /**
+     * To get the meaningful text of the crate for the users.
+     * @return the meaningful text
+     */
+    @NonNull
+    public CharSequence getLabel() {
+        if (TextUtils.isEmpty(mLabel)) {
+            return mId;
+        }
+        return mLabel;
+    }
+
+
+    /**
+     * To return the expiration time.
+     * <p>
+     *     If the current time is larger than expiration time, the crate files are considered to be
+     *     deleted.
+     * </p>
+     * @return the expiration time
+     */
+    @CurrentTimeMillisLong
+    public long getExpirationMillis() {
+        return mExpiration;
+    }
+
+    /**
+     * To set the expiration time.
+     * @param expiration the expiration time
+     * @hide
+     */
+    public void setExpiration(@CurrentTimeMillisLong long expiration) {
+        Preconditions.checkArgumentNonnegative(expiration);
+        mExpiration = expiration;
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+    /**
+     * To compare with crateinfo when selves' mId is validated.
+     * <p>The validated crateinfo.mId must be validated the following items.
+     * <ul>
+     *     <li>mId is not null</li>
+     *     <li>mId is not empty string</li>
+     * </ul>
+     * </p>
+     * @param   obj   the reference object with which to compare.
+     * @return true when selves's mId is validated and equal to crateinfo.mId.
+     */
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj instanceof CrateInfo) {
+            CrateInfo crateInfo = (CrateInfo) obj;
+            if (!TextUtils.isEmpty(mId)
+                    && TextUtils.equals(mId, crateInfo.mId)) {
+                return true;
+            }
+        }
+
+        return super.equals(obj);
+    }
+
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        if (dest == null) {
+            return;
+        }
+
+        dest.writeCharSequence(mLabel);
+        dest.writeLong(mExpiration);
+
+        dest.writeInt(mUid);
+        dest.writeString(mPackageName);
+        dest.writeString(mId);
+    }
+
+    /**
+     * To read the data from parcel.
+     * <p>
+     *     It's called by StorageStatsService.
+     * </p>
+     * @hide
+     */
+    public void readFromParcel(@Nullable Parcel in) {
+        if (in == null) {
+            return;
+        }
+
+        mLabel = in.readCharSequence();
+        mExpiration = in.readLong();
+
+        mUid = in.readInt();
+        mPackageName = in.readString();
+        mId = in.readString();
+    }
+
+    @NonNull
+    public static final Creator<CrateInfo> CREATOR = new Creator<CrateInfo>() {
+        @NonNull
+        @Override
+        public CrateInfo createFromParcel(@NonNull Parcel in) {
+            CrateInfo crateInfo = new CrateInfo();
+            crateInfo.readFromParcel(in);
+            return crateInfo;
+        }
+
+        @NonNull
+        @Override
+        public CrateInfo[] newArray(int size) {
+            return new CrateInfo[size];
+        }
+    };
+
+    /**
+     * To copy the information from service into crateinfo.
+     * <p>
+     * This function is called in system_server. The copied information includes
+     *     <ul>
+     *         <li>uid</li>
+     *         <li>package name</li>
+     *         <li>crate id</li>
+     *     </ul>
+     * </p>
+     * @param uid the uid that the crate belong to
+     * @param packageName the package name that the crate belong to
+     * @param id the crate dir
+     * @return the CrateInfo instance
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public static CrateInfo copyFrom(int uid, @Nullable String packageName, @Nullable String id) {
+        if (!UserHandle.isApp(uid) || TextUtils.isEmpty(packageName) || TextUtils.isEmpty(id)) {
+            return null;
+        }
+
+        CrateInfo crateInfo = new CrateInfo(id /* default label = id */, 0);
+        crateInfo.mUid = uid;
+        crateInfo.mPackageName = packageName;
+        crateInfo.mId = id;
+        return crateInfo;
+    }
+}
diff --git a/android-34/android/os/storage/DiskInfo.java b/android-34/android/os/storage/DiskInfo.java
new file mode 100644
index 0000000..d32928c
--- /dev/null
+++ b/android-34/android/os/storage/DiskInfo.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 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.os.storage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.DebugUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+
+import java.io.CharArrayWriter;
+import java.util.Objects;
+
+/**
+ * Information about a physical disk which may contain one or more
+ * {@link VolumeInfo}.
+ *
+ * @hide
+ */
+public class DiskInfo implements Parcelable {
+    public static final String ACTION_DISK_SCANNED =
+            "android.os.storage.action.DISK_SCANNED";
+    public static final String EXTRA_DISK_ID =
+            "android.os.storage.extra.DISK_ID";
+    public static final String EXTRA_VOLUME_COUNT =
+            "android.os.storage.extra.VOLUME_COUNT";
+
+    public static final int FLAG_ADOPTABLE = 1 << 0;
+    public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
+    public static final int FLAG_SD = 1 << 2;
+    public static final int FLAG_USB = 1 << 3;
+    /** The FLAG_STUB_VISIBLE is set from vold, which gets the flag from outside (e.g., ChromeOS) */
+    public static final int FLAG_STUB_VISIBLE = 1 << 6;
+
+    public final String id;
+    @UnsupportedAppUsage
+    public final int flags;
+    @UnsupportedAppUsage
+    public long size;
+    @UnsupportedAppUsage
+    public String label;
+    /** Hacky; don't rely on this count */
+    public int volumeCount;
+    public String sysPath;
+
+    public DiskInfo(String id, int flags) {
+        this.id = Preconditions.checkNotNull(id);
+        this.flags = flags;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public DiskInfo(Parcel parcel) {
+        id = parcel.readString();
+        flags = parcel.readInt();
+        size = parcel.readLong();
+        label = parcel.readString();
+        volumeCount = parcel.readInt();
+        sysPath = parcel.readString();
+    }
+
+    @UnsupportedAppUsage
+    public @NonNull String getId() {
+        return id;
+    }
+
+    private boolean isInteresting(String label) {
+        if (TextUtils.isEmpty(label)) {
+            return false;
+        }
+        if (label.equalsIgnoreCase("ata")) {
+            return false;
+        }
+        if (label.toLowerCase().contains("generic")) {
+            return false;
+        }
+        if (label.toLowerCase().startsWith("usb")) {
+            return false;
+        }
+        if (label.toLowerCase().startsWith("multiple")) {
+            return false;
+        }
+        return true;
+    }
+
+    @UnsupportedAppUsage
+    public @Nullable String getDescription() {
+        final Resources res = Resources.getSystem();
+        if ((flags & FLAG_SD) != 0) {
+            if (isInteresting(label)) {
+                return res.getString(com.android.internal.R.string.storage_sd_card_label, label);
+            } else {
+                return res.getString(com.android.internal.R.string.storage_sd_card);
+            }
+        } else if ((flags & FLAG_USB) != 0) {
+            if (isInteresting(label)) {
+                return res.getString(com.android.internal.R.string.storage_usb_drive_label, label);
+            } else {
+                return res.getString(com.android.internal.R.string.storage_usb_drive);
+            }
+        } else {
+            return null;
+        }
+    }
+
+    public @Nullable String getShortDescription() {
+        final Resources res = Resources.getSystem();
+        if (isSd()) {
+            return res.getString(com.android.internal.R.string.storage_sd_card);
+        } else if (isUsb()) {
+            return res.getString(com.android.internal.R.string.storage_usb_drive);
+        } else {
+            return null;
+        }
+    }
+
+    @UnsupportedAppUsage
+    public boolean isAdoptable() {
+        return (flags & FLAG_ADOPTABLE) != 0;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isDefaultPrimary() {
+        return (flags & FLAG_DEFAULT_PRIMARY) != 0;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isSd() {
+        return (flags & FLAG_SD) != 0;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isUsb() {
+        return (flags & FLAG_USB) != 0;
+    }
+
+    public boolean isStubVisible() {
+        return (flags & FLAG_STUB_VISIBLE) != 0;
+    }
+
+    @Override
+    public String toString() {
+        final CharArrayWriter writer = new CharArrayWriter();
+        dump(new IndentingPrintWriter(writer, "    ", 80));
+        return writer.toString();
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("DiskInfo{" + id + "}:");
+        pw.increaseIndent();
+        pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
+        pw.printPair("size", size);
+        pw.printPair("label", label);
+        pw.println();
+        pw.printPair("sysPath", sysPath);
+        pw.decreaseIndent();
+        pw.println();
+    }
+
+    @Override
+    public DiskInfo clone() {
+        final Parcel temp = Parcel.obtain();
+        try {
+            writeToParcel(temp, 0);
+            temp.setDataPosition(0);
+            return CREATOR.createFromParcel(temp);
+        } finally {
+            temp.recycle();
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof DiskInfo) {
+            return Objects.equals(id, ((DiskInfo) o).id);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static final @android.annotation.NonNull Creator<DiskInfo> CREATOR = new Creator<DiskInfo>() {
+        @Override
+        public DiskInfo createFromParcel(Parcel in) {
+            return new DiskInfo(in);
+        }
+
+        @Override
+        public DiskInfo[] newArray(int size) {
+            return new DiskInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(id);
+        parcel.writeInt(this.flags);
+        parcel.writeLong(size);
+        parcel.writeString(label);
+        parcel.writeInt(volumeCount);
+        parcel.writeString(sysPath);
+    }
+}
diff --git a/android-34/android/os/storage/OnObbStateChangeListener.java b/android-34/android/os/storage/OnObbStateChangeListener.java
new file mode 100644
index 0000000..1fb1782
--- /dev/null
+++ b/android-34/android/os/storage/OnObbStateChangeListener.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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.os.storage;
+
+/**
+ * Used for receiving notifications from {@link StorageManager} about OBB file
+ * states.
+ */
+public abstract class OnObbStateChangeListener {
+
+    /**
+     * The OBB container is now mounted and ready for use. Returned in status
+     * messages from calls made via {@link StorageManager}
+     */
+    public static final int MOUNTED = 1;
+
+    /**
+     * The OBB container is now unmounted and not usable. Returned in status
+     * messages from calls made via {@link StorageManager}
+     */
+    public static final int UNMOUNTED = 2;
+
+    /**
+     * There was an internal system error encountered while trying to mount the
+     * OBB. Returned in status messages from calls made via
+     * {@link StorageManager}
+     */
+    public static final int ERROR_INTERNAL = 20;
+
+    /**
+     * The OBB could not be mounted by the system. Returned in status messages
+     * from calls made via {@link StorageManager}
+     */
+    public static final int ERROR_COULD_NOT_MOUNT = 21;
+
+    /**
+     * The OBB could not be unmounted. This most likely indicates that a file is
+     * in use on the OBB. Returned in status messages from calls made via
+     * {@link StorageManager}
+     */
+    public static final int ERROR_COULD_NOT_UNMOUNT = 22;
+
+    /**
+     * A call was made to unmount the OBB when it was not mounted. Returned in
+     * status messages from calls made via {@link StorageManager}
+     */
+    public static final int ERROR_NOT_MOUNTED = 23;
+
+    /**
+     * The OBB has already been mounted. Returned in status messages from calls
+     * made via {@link StorageManager}
+     */
+    public static final int ERROR_ALREADY_MOUNTED = 24;
+
+    /**
+     * The current application does not have permission to use this OBB. This
+     * could be because the OBB indicates it's owned by a different package or
+     * some other error. Returned in status messages from calls made via
+     * {@link StorageManager}
+     */
+    public static final int ERROR_PERMISSION_DENIED = 25;
+
+    /**
+     * Called when an OBB has changed states.
+     * 
+     * @param path path to the OBB file the state change has happened on
+     * @param state the current state of the OBB
+     */
+    public void onObbStateChange(String path, int state) {
+    }
+}
diff --git a/android-34/android/os/storage/StorageEventListener.java b/android-34/android/os/storage/StorageEventListener.java
new file mode 100644
index 0000000..694ff19
--- /dev/null
+++ b/android-34/android/os/storage/StorageEventListener.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ * 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.os.storage;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+
+/**
+ * Used for receiving notifications from the StorageManager
+ * 
+ * @hide
+ */
+public class StorageEventListener {
+
+    @UnsupportedAppUsage
+    public StorageEventListener() {
+    }
+
+    /**
+     * Called when the detection state of a USB Mass Storage host has changed.
+     * @param connected true if the USB mass storage is connected.
+     */
+    @UnsupportedAppUsage
+    public void onUsbMassStorageConnectionChanged(boolean connected) {
+    }
+
+    /**
+     * Called when storage has changed state
+     * @param path the filesystem path for the storage
+     * @param oldState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
+     * @param newState the old state as returned by {@link android.os.Environment#getExternalStorageState()}.
+     */
+    @UnsupportedAppUsage
+    public void onStorageStateChanged(String path, String oldState, String newState) {
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void onVolumeRecordChanged(VolumeRecord rec) {
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void onVolumeForgotten(String fsUuid) {
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void onDiskScanned(DiskInfo disk, int volumeCount) {
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void onDiskDestroyed(DiskInfo disk) {
+    }
+}
diff --git a/android-34/android/os/storage/StorageManager.java b/android-34/android/os/storage/StorageManager.java
new file mode 100644
index 0000000..80dd488
--- /dev/null
+++ b/android-34/android/os/storage/StorageManager.java
@@ -0,0 +1,2911 @@
+/*
+ * 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.
+ * 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.os.storage;
+
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
+import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.UserHandle.PER_USER_RANGE;
+
+import android.annotation.BytesLong;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.WorkerThread;
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageMoveObserver;
+import android.content.pm.PackageManager;
+import android.content.res.ObbInfo;
+import android.content.res.ObbScanner;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IInstalld;
+import android.os.IVold;
+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.ProxyFileDescriptorCallback;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.MediaStore;
+import android.provider.Settings;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.text.TextUtils;
+import android.util.DataUnit;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.AppFuseMount;
+import com.android.internal.os.FuseAppLoop;
+import com.android.internal.os.FuseUnavailableMountException;
+import com.android.internal.os.RoSystemProperties;
+import com.android.internal.util.Preconditions;
+
+import dalvik.system.BlockGuard;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * StorageManager is the interface to the systems storage service. The storage
+ * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
+ * <p>
+ * OBBs contain a filesystem that maybe be encrypted on disk and mounted
+ * on-demand from an application. OBBs are a good way of providing large amounts
+ * of binary assets without packaging them into APKs as they may be multiple
+ * gigabytes in size. However, due to their size, they're most likely stored in
+ * a shared storage pool accessible from all programs. The system does not
+ * guarantee the security of the OBB file itself: if any program modifies the
+ * OBB, there is no guarantee that a read from that OBB will produce the
+ * expected output.
+ */
+@SystemService(Context.STORAGE_SERVICE)
+public class StorageManager {
+    private static final String TAG = "StorageManager";
+    private static final boolean LOCAL_LOGV = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** {@hide} */
+    public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
+    /** {@hide} */
+    public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
+    /** {@hide} */
+    public static final String PROP_HAS_RESERVED = "vold.has_reserved";
+    /** {@hide} */
+    public static final String PROP_ADOPTABLE = "persist.sys.adoptable";
+    /** {@hide} */
+    public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
+    /** {@hide} */
+    public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
+    /** {@hide} */
+    public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST =
+            "forced_scoped_storage_whitelist";
+
+    /** {@hide} */
+    public static final String UUID_PRIVATE_INTERNAL = null;
+    /** {@hide} */
+    public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
+    /** {@hide} */
+    public static final String UUID_SYSTEM = "system";
+
+    // NOTE: See comments around #convert for more details.
+    private static final String FAT_UUID_PREFIX = "fafafafa-fafa-5afa-8afa-fafa";
+
+    // NOTE: UUID constants below are namespaced
+    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad default
+    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad primary_physical
+    // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad system
+
+    /**
+     * UUID representing the default internal storage of this device which
+     * provides {@link Environment#getDataDirectory()}.
+     * <p>
+     * This value is constant across all devices and it will never change, and
+     * thus it cannot be used to uniquely identify a particular physical device.
+     *
+     * @see #getUuidForPath(File)
+     * @see ApplicationInfo#storageUuid
+     */
+    public static final UUID UUID_DEFAULT = UUID
+            .fromString("41217664-9172-527a-b3d5-edabb50a7d69");
+
+    /** {@hide} */
+    public static final UUID UUID_PRIMARY_PHYSICAL_ = UUID
+            .fromString("0f95a519-dae7-5abf-9519-fbd6209e05fd");
+
+    /** {@hide} */
+    public static final UUID UUID_SYSTEM_ = UUID
+            .fromString("5d258386-e60d-59e3-826d-0089cdd42cc0");
+
+    /**
+     * Activity Action: Allows the user to manage their storage. This activity
+     * provides the ability to free up space on the device by deleting data such
+     * as apps.
+     * <p>
+     * If the sending application has a specific storage device or allocation
+     * size in mind, they can optionally define {@link #EXTRA_UUID} or
+     * {@link #EXTRA_REQUESTED_BYTES}, respectively.
+     * <p>
+     * This intent should be launched using
+     * {@link Activity#startActivityForResult(Intent, int)} so that the user
+     * knows which app is requesting the storage space. The returned result will
+     * be {@link Activity#RESULT_OK} if the requested space was made available,
+     * or {@link Activity#RESULT_CANCELED} otherwise.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
+
+    /**
+     * Activity Action: Allows the user to free up space by clearing app external cache directories.
+     * The intent doesn't automatically clear cache, but shows a dialog and lets the user decide.
+     * <p>
+     * This intent should be launched using
+     * {@link Activity#startActivityForResult(Intent, int)} so that the user
+     * knows which app is requesting to clear cache. The returned result will be:
+     * {@link Activity#RESULT_OK} if the activity was launched and all cache was cleared,
+     * {@link OsConstants#EIO} if an error occurred while clearing the cache or
+     * {@link Activity#RESULT_CANCELED} otherwise.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE";
+
+    /**
+     * Extra {@link UUID} used to indicate the storage volume where an
+     * application is interested in allocating or managing disk space.
+     *
+     * @see #ACTION_MANAGE_STORAGE
+     * @see #UUID_DEFAULT
+     * @see #getUuidForPath(File)
+     * @see Intent#putExtra(String, java.io.Serializable)
+     */
+    public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
+
+    /**
+     * Extra used to indicate the total size (in bytes) that an application is
+     * interested in allocating.
+     * <p>
+     * When defined, the management UI will help guide the user to free up
+     * enough disk space to reach this requested value.
+     *
+     * @see #ACTION_MANAGE_STORAGE
+     */
+    public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
+
+    /** {@hide} */
+    public static final int DEBUG_ADOPTABLE_FORCE_ON = 1 << 0;
+    /** {@hide} */
+    public static final int DEBUG_ADOPTABLE_FORCE_OFF = 1 << 1;
+    /** {@hide} */
+    public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
+    /** {@hide} */
+    public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
+    /** {@hide} */
+    public static final int DEBUG_VIRTUAL_DISK = 1 << 4;
+
+    /** {@hide} */
+    public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
+    /** {@hide} */
+    public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
+    /** {@hide} */
+    public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL;
+    /** @hide */
+    public static final int FLAG_STORAGE_SDK = IInstalld.FLAG_STORAGE_SDK;
+
+    /** {@hide} */
+    @IntDef(prefix = "FLAG_STORAGE_",  value = {
+            FLAG_STORAGE_DE,
+            FLAG_STORAGE_CE,
+            FLAG_STORAGE_EXTERNAL,
+            FLAG_STORAGE_SDK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StorageFlags {}
+
+    /** {@hide} */
+    public static final int FLAG_FOR_WRITE = 1 << 8;
+    /** {@hide} */
+    public static final int FLAG_REAL_STATE = 1 << 9;
+    /** {@hide} */
+    public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
+    /** {@hide} */
+    public static final int FLAG_INCLUDE_RECENT = 1 << 11;
+    /** {@hide} */
+    public static final int FLAG_INCLUDE_SHARED_PROFILE = 1 << 12;
+
+    /** {@hide} */
+    public static final int FSTRIM_FLAG_DEEP = IVold.FSTRIM_FLAG_DEEP_TRIM;
+
+    /** @hide The volume is not encrypted. */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int ENCRYPTION_STATE_NONE = 1;
+
+    private static volatile IStorageManager sStorageManager = null;
+
+    private final Context mContext;
+    private final ContentResolver mResolver;
+
+    private final IStorageManager mStorageManager;
+    private final AppOpsManager mAppOps;
+    private final Looper mLooper;
+    private final AtomicInteger mNextNonce = new AtomicInteger(0);
+
+    @GuardedBy("mDelegates")
+    private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
+
+    private class StorageEventListenerDelegate extends IStorageEventListener.Stub {
+        final Executor mExecutor;
+        final StorageEventListener mListener;
+        final StorageVolumeCallback mCallback;
+
+        public StorageEventListenerDelegate(@NonNull Executor executor,
+                @NonNull StorageEventListener listener, @NonNull StorageVolumeCallback callback) {
+            mExecutor = executor;
+            mListener = listener;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
+            mExecutor.execute(() -> {
+                mListener.onUsbMassStorageConnectionChanged(connected);
+            });
+        }
+
+        @Override
+        public void onStorageStateChanged(String path, String oldState, String newState) {
+            mExecutor.execute(() -> {
+                mListener.onStorageStateChanged(path, oldState, newState);
+
+                if (path != null) {
+                    for (StorageVolume sv : getStorageVolumes()) {
+                        if (Objects.equals(path, sv.getPath())) {
+                            mCallback.onStateChanged(sv);
+                        }
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+            mExecutor.execute(() -> {
+                mListener.onVolumeStateChanged(vol, oldState, newState);
+
+                final File path = vol.getPathForUser(UserHandle.myUserId());
+                if (path != null) {
+                    for (StorageVolume sv : getStorageVolumes()) {
+                        if (Objects.equals(path.getAbsolutePath(), sv.getPath())) {
+                            mCallback.onStateChanged(sv);
+                        }
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onVolumeRecordChanged(VolumeRecord rec) {
+            mExecutor.execute(() -> {
+                mListener.onVolumeRecordChanged(rec);
+            });
+        }
+
+        @Override
+        public void onVolumeForgotten(String fsUuid) {
+            mExecutor.execute(() -> {
+                mListener.onVolumeForgotten(fsUuid);
+            });
+        }
+
+        @Override
+        public void onDiskScanned(DiskInfo disk, int volumeCount) {
+            mExecutor.execute(() -> {
+                mListener.onDiskScanned(disk, volumeCount);
+            });
+        }
+
+        @Override
+        public void onDiskDestroyed(DiskInfo disk) throws RemoteException {
+            mExecutor.execute(() -> {
+                mListener.onDiskDestroyed(disk);
+            });
+        }
+    }
+
+    /**
+     * Binder listener for OBB action results.
+     */
+    private final ObbActionListener mObbActionListener = new ObbActionListener();
+
+    private class ObbActionListener extends IObbActionListener.Stub {
+        @SuppressWarnings("hiding")
+        private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
+
+        @Override
+        public void onObbResult(String filename, int nonce, int status) {
+            final ObbListenerDelegate delegate;
+            synchronized (mListeners) {
+                delegate = mListeners.get(nonce);
+                if (delegate != null) {
+                    mListeners.remove(nonce);
+                }
+            }
+
+            if (delegate != null) {
+                delegate.sendObbStateChanged(filename, status);
+            }
+        }
+
+        public int addListener(OnObbStateChangeListener listener) {
+            final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
+
+            synchronized (mListeners) {
+                mListeners.put(delegate.nonce, delegate);
+            }
+
+            return delegate.nonce;
+        }
+    }
+
+    private int getNextNonce() {
+        return mNextNonce.getAndIncrement();
+    }
+
+    /**
+     * Private class containing sender and receiver code for StorageEvents.
+     */
+    private class ObbListenerDelegate {
+        private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
+        private final Handler mHandler;
+
+        private final int nonce;
+
+        ObbListenerDelegate(OnObbStateChangeListener listener) {
+            nonce = getNextNonce();
+            mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
+            mHandler = new Handler(mLooper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    final OnObbStateChangeListener changeListener = getListener();
+                    if (changeListener == null) {
+                        return;
+                    }
+
+                    changeListener.onObbStateChange((String) msg.obj, msg.arg1);
+                }
+            };
+        }
+
+        OnObbStateChangeListener getListener() {
+            if (mObbEventListenerRef == null) {
+                return null;
+            }
+            return mObbEventListenerRef.get();
+        }
+
+        void sendObbStateChanged(String path, int state) {
+            mHandler.obtainMessage(0, state, 0, path).sendToTarget();
+        }
+    }
+
+    /** {@hide} */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static StorageManager from(Context context) {
+        return context.getSystemService(StorageManager.class);
+    }
+
+    /**
+     * Constructs a StorageManager object through which an application can
+     * can communicate with the systems mount service.
+     *
+     * @param looper The {@link android.os.Looper} which events will be received on.
+     *
+     * <p>Applications can get instance of this class by calling
+     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
+     * of {@link android.content.Context#STORAGE_SERVICE}.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public StorageManager(Context context, Looper looper) throws ServiceNotFoundException {
+        mContext = context;
+        mResolver = context.getContentResolver();
+        mLooper = looper;
+        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount"));
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
+    }
+
+    /**
+     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
+     *
+     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void registerListener(StorageEventListener listener) {
+        synchronized (mDelegates) {
+            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(
+                    mContext.getMainExecutor(), listener, new StorageVolumeCallback());
+            try {
+                mStorageManager.registerListener(delegate);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mDelegates.add(delegate);
+        }
+    }
+
+    /**
+     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
+     *
+     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void unregisterListener(StorageEventListener listener) {
+        synchronized (mDelegates) {
+            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
+                final StorageEventListenerDelegate delegate = i.next();
+                if (delegate.mListener == listener) {
+                    try {
+                        mStorageManager.unregisterListener(delegate);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * Callback that delivers {@link StorageVolume} related events.
+     * <p>
+     * For example, this can be used to detect when a volume changes to the
+     * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED}
+     * states.
+     *
+     * @see StorageManager#registerStorageVolumeCallback
+     * @see StorageManager#unregisterStorageVolumeCallback
+     */
+    public static class StorageVolumeCallback {
+        /**
+         * Called when {@link StorageVolume#getState()} changes, such as
+         * changing to the {@link Environment#MEDIA_MOUNTED} or
+         * {@link Environment#MEDIA_UNMOUNTED} states.
+         * <p>
+         * The given argument is a snapshot in time and can be used to process
+         * events in the order they occurred, or you can call
+         * {@link StorageManager#getStorageVolumes()} to observe the latest
+         * value.
+         */
+        public void onStateChanged(@NonNull StorageVolume volume) { }
+    }
+
+    /**
+     * Registers the given callback to listen for {@link StorageVolume} changes.
+     * <p>
+     * For example, this can be used to detect when a volume changes to the
+     * {@link Environment#MEDIA_MOUNTED} or {@link Environment#MEDIA_UNMOUNTED}
+     * states.
+     *
+     * @see StorageManager#unregisterStorageVolumeCallback
+     */
+    public void registerStorageVolumeCallback(@CallbackExecutor @NonNull Executor executor,
+            @NonNull StorageVolumeCallback callback) {
+        synchronized (mDelegates) {
+            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(
+                    executor, new StorageEventListener(), callback);
+            try {
+                mStorageManager.registerListener(delegate);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mDelegates.add(delegate);
+        }
+    }
+
+    /**
+     * Unregisters the given callback from listening for {@link StorageVolume}
+     * changes.
+     *
+     * @see StorageManager#registerStorageVolumeCallback
+     */
+    public void unregisterStorageVolumeCallback(@NonNull StorageVolumeCallback callback) {
+        synchronized (mDelegates) {
+            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
+                final StorageEventListenerDelegate delegate = i.next();
+                if (delegate.mCallback == callback) {
+                    try {
+                        mStorageManager.unregisterListener(delegate);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * Enables USB Mass Storage (UMS) on the device.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void enableUsbMassStorage() {
+    }
+
+    /**
+     * Disables USB Mass Storage (UMS) on the device.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void disableUsbMassStorage() {
+    }
+
+    /**
+     * Query if a USB Mass Storage (UMS) host is connected.
+     * @return true if UMS host is connected.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean isUsbMassStorageConnected() {
+        return false;
+    }
+
+    /**
+     * Query if a USB Mass Storage (UMS) is enabled on the device.
+     * @return true if UMS host is enabled.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public boolean isUsbMassStorageEnabled() {
+        return false;
+    }
+
+    /**
+     * Mount an Opaque Binary Blob (OBB) file.
+     * <p>
+     * The OBB will remain mounted for as long as the StorageManager reference
+     * is held by the application. As soon as this reference is lost, the OBBs
+     * in use will be unmounted. The {@link OnObbStateChangeListener} registered
+     * with this call will receive the success or failure of this operation.
+     * <p>
+     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+     * file matches a package ID that is owned by the calling program's UID.
+     * That is, shared UID applications can attempt to mount any other
+     * application's OBB that shares its UID.
+     *
+     * @param rawPath the path to the OBB file
+     * @param key must be <code>null</code>. Previously, some Android device
+     *            implementations accepted a non-<code>null</code> key to mount
+     *            an encrypted OBB file. However, this never worked reliably and
+     *            is no longer supported.
+     * @param listener will receive the success or failure of the operation
+     * @return whether the mount call was successfully queued or not
+     */
+    public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
+        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Preconditions.checkArgument(key == null, "mounting encrypted OBBs is no longer supported");
+        Preconditions.checkNotNull(listener, "listener cannot be null");
+
+        try {
+            final String canonicalPath = new File(rawPath).getCanonicalPath();
+            final int nonce = mObbActionListener.addListener(listener);
+            mStorageManager.mountObb(rawPath, canonicalPath, mObbActionListener, nonce,
+                    getObbInfo(canonicalPath));
+            return true;
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a {@link PendingIntent} that can be used by Apps with
+     * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission
+     * to launch the manageSpaceActivity for any App that implements it, irrespective of its
+     * exported status.
+     * <p>
+     * Caller has the responsibility of supplying a valid packageName which has
+     * manageSpaceActivity implemented.
+     *
+     * @param packageName package name for the App for which manageSpaceActivity is to be launched
+     * @param requestCode for launching the activity
+     * @return PendingIntent to launch the manageSpaceActivity if successful, null if the
+     * packageName doesn't have a manageSpaceActivity.
+     * @throws IllegalArgumentException an invalid packageName is supplied.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+    @Nullable
+    public PendingIntent getManageSpaceActivityIntent(
+            @NonNull String packageName, int requestCode) {
+        try {
+            return mStorageManager.getManageSpaceActivityIntent(packageName,
+                    requestCode);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private ObbInfo getObbInfo(String canonicalPath) {
+        try {
+            final ObbInfo obbInfo = ObbScanner.getObbInfo(canonicalPath);
+            return obbInfo;
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Couldn't get OBB info for " + canonicalPath, e);
+        }
+    }
+
+    /**
+     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
+     * <code>force</code> flag is true, it will kill any application needed to
+     * unmount the given OBB (even the calling application).
+     * <p>
+     * The {@link OnObbStateChangeListener} registered with this call will
+     * receive the success or failure of this operation.
+     * <p>
+     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+     * file matches a package ID that is owned by the calling program's UID.
+     * That is, shared UID applications can obtain access to any other
+     * application's OBB that shares its UID.
+     * <p>
+     *
+     * @param rawPath path to the OBB file
+     * @param force whether to kill any programs using this in order to unmount
+     *            it
+     * @param listener will receive the success or failure of the operation
+     * @return whether the unmount call was successfully queued or not
+     */
+    public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
+        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Preconditions.checkNotNull(listener, "listener cannot be null");
+
+        try {
+            final int nonce = mObbActionListener.addListener(listener);
+            mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce);
+            return true;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
+     *
+     * @param rawPath path to OBB image
+     * @return true if OBB is mounted; false if not mounted or on error
+     */
+    public boolean isObbMounted(String rawPath) {
+        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+
+        try {
+            return mStorageManager.isObbMounted(rawPath);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
+     * give you the path to where you can obtain access to the internals of the
+     * OBB.
+     *
+     * @param rawPath path to OBB image
+     * @return absolute path to mounted OBB image data or <code>null</code> if
+     *         not mounted or exception encountered trying to read status
+     */
+    public String getMountedObbPath(String rawPath) {
+        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+
+        try {
+            return mStorageManager.getMountedObbPath(rawPath);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public @NonNull List<DiskInfo> getDisks() {
+        try {
+            return Arrays.asList(mStorageManager.getDisks());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public @Nullable DiskInfo findDiskById(String id) {
+        Preconditions.checkNotNull(id);
+        // TODO; go directly to service to make this faster
+        for (DiskInfo disk : getDisks()) {
+            if (Objects.equals(disk.id, id)) {
+                return disk;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @Nullable VolumeInfo findVolumeById(String id) {
+        Preconditions.checkNotNull(id);
+        // TODO; go directly to service to make this faster
+        for (VolumeInfo vol : getVolumes()) {
+            if (Objects.equals(vol.id, id)) {
+                return vol;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
+        Preconditions.checkNotNull(fsUuid);
+        // TODO; go directly to service to make this faster
+        for (VolumeInfo vol : getVolumes()) {
+            if (Objects.equals(vol.fsUuid, fsUuid)) {
+                return vol;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
+        Preconditions.checkNotNull(fsUuid);
+        // TODO; go directly to service to make this faster
+        for (VolumeRecord rec : getVolumeRecords()) {
+            if (Objects.equals(rec.fsUuid, fsUuid)) {
+                return rec;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
+        if (emulatedVol != null) {
+            String id = emulatedVol.getId();
+            int idx = id.indexOf(";");
+            if (idx != -1) {
+                id = id.substring(0, idx);
+            }
+            return findVolumeById(id.replace("emulated", "private"));
+        } else {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
+        if (privateVol != null) {
+            return findVolumeById(privateVol.getId().replace("private", "emulated") + ";"
+                    + mContext.getUserId());
+        } else {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
+        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
+            return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
+        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
+            return getPrimaryPhysicalVolume();
+        } else {
+            return findVolumeByUuid(volumeUuid);
+        }
+    }
+
+    /**
+     * Return a UUID identifying the storage volume that hosts the given
+     * filesystem path.
+     * <p>
+     * If this path is hosted by the default internal storage of the device at
+     * {@link Environment#getDataDirectory()}, the returned value will be
+     * {@link #UUID_DEFAULT}.
+     *
+     * @throws IOException when the storage device hosting the given path isn't
+     *             present, or when it doesn't have a valid UUID.
+     */
+    public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
+        Preconditions.checkNotNull(path);
+        final String pathString = path.getCanonicalPath();
+        if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
+            return UUID_DEFAULT;
+        }
+        try {
+            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
+                if (vol.path != null && FileUtils.contains(vol.path, pathString)
+                        && vol.type != VolumeInfo.TYPE_PUBLIC && vol.type != VolumeInfo.TYPE_STUB) {
+                    // TODO: verify that emulated adopted devices have UUID of
+                    // underlying volume
+                    try {
+                        return convert(vol.fsUuid);
+                    } catch (IllegalArgumentException e) {
+                        continue;
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        throw new FileNotFoundException("Failed to find a storage device for " + path);
+    }
+
+    /** {@hide} */
+    public @NonNull File findPathForUuid(String volumeUuid) throws FileNotFoundException {
+        final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid);
+        if (vol != null) {
+            return vol.getPath();
+        }
+        throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid);
+    }
+
+    /**
+     * Test if the given file descriptor supports allocation of disk space using
+     * {@link #allocateBytes(FileDescriptor, long)}.
+     */
+    public boolean isAllocationSupported(@NonNull FileDescriptor fd) {
+        try {
+            getUuidForPath(ParcelFileDescriptor.getFile(fd));
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public @NonNull List<VolumeInfo> getVolumes() {
+        try {
+            return Arrays.asList(mStorageManager.getVolumes(0));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public @NonNull List<VolumeInfo> getWritablePrivateVolumes() {
+        try {
+            final ArrayList<VolumeInfo> res = new ArrayList<>();
+            for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
+                if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
+                    res.add(vol);
+                }
+            }
+            return res;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public @NonNull List<VolumeRecord> getVolumeRecords() {
+        try {
+            return Arrays.asList(mStorageManager.getVolumeRecords(0));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
+        if (vol == null) return null;
+
+        // Nickname always takes precedence when defined
+        if (!TextUtils.isEmpty(vol.fsUuid)) {
+            final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
+            if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
+                return rec.nickname;
+            }
+        }
+
+        if (!TextUtils.isEmpty(vol.getDescription())) {
+            return vol.getDescription();
+        }
+
+        if (vol.disk != null) {
+            return vol.disk.getDescription();
+        }
+
+        return null;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
+        final List<VolumeInfo> vols = getVolumes();
+        for (VolumeInfo vol : vols) {
+            if (vol.isPrimaryPhysical()) {
+                return vol;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    public void mount(String volId) {
+        try {
+            mStorageManager.mount(volId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public void unmount(String volId) {
+        try {
+            mStorageManager.unmount(volId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void format(String volId) {
+        try {
+            mStorageManager.format(volId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public long benchmark(String volId) {
+        final CompletableFuture<PersistableBundle> result = new CompletableFuture<>();
+        benchmark(volId, new IVoldTaskListener.Stub() {
+            @Override
+            public void onStatus(int status, PersistableBundle extras) {
+                // Ignored
+            }
+
+            @Override
+            public void onFinished(int status, PersistableBundle extras) {
+                result.complete(extras);
+            }
+        });
+        try {
+            // Convert ms to ns
+            return result.get(3, TimeUnit.MINUTES).getLong("run", Long.MAX_VALUE) * 1000000;
+        } catch (Exception e) {
+            return Long.MAX_VALUE;
+        }
+    }
+
+    /** {@hide} */
+    public void benchmark(String volId, IVoldTaskListener listener) {
+        try {
+            mStorageManager.benchmark(volId, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void partitionPublic(String diskId) {
+        try {
+            mStorageManager.partitionPublic(diskId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void partitionPrivate(String diskId) {
+        try {
+            mStorageManager.partitionPrivate(diskId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void partitionMixed(String diskId, int ratio) {
+        try {
+            mStorageManager.partitionMixed(diskId, ratio);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void wipeAdoptableDisks() {
+        // We only wipe devices in "adoptable" locations, which are in a
+        // long-term stable slot/location on the device, where apps have a
+        // reasonable chance of storing sensitive data. (Apps need to go through
+        // SAF to write to transient volumes.)
+        final List<DiskInfo> disks = getDisks();
+        for (DiskInfo disk : disks) {
+            final String diskId = disk.getId();
+            if (disk.isAdoptable()) {
+                Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
+                try {
+                    // TODO: switch to explicit wipe command when we have it,
+                    // for now rely on the fact that vfat format does a wipe
+                    mStorageManager.partitionPublic(diskId);
+                } catch (Exception e) {
+                    Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
+                }
+            } else {
+                Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
+            }
+        }
+    }
+
+    /** {@hide} */
+    public void setVolumeNickname(String fsUuid, String nickname) {
+        try {
+            mStorageManager.setVolumeNickname(fsUuid, nickname);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void setVolumeInited(String fsUuid, boolean inited) {
+        try {
+            mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
+                    VolumeRecord.USER_FLAG_INITED);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
+        try {
+            mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
+                    VolumeRecord.USER_FLAG_SNOOZED);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void forgetVolume(String fsUuid) {
+        try {
+            mStorageManager.forgetVolume(fsUuid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This is not the API you're looking for.
+     *
+     * @see PackageManager#getPrimaryStorageCurrentVolume()
+     * @hide
+     */
+    public String getPrimaryStorageUuid() {
+        try {
+            return mStorageManager.getPrimaryStorageUuid();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This is not the API you're looking for.
+     *
+     * @see PackageManager#movePrimaryStorage(VolumeInfo)
+     * @hide
+     */
+    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
+        try {
+            mStorageManager.setPrimaryStorageUuid(volumeUuid, callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the {@link StorageVolume} that contains the given file, or
+     * {@code null} if none.
+     */
+    public @Nullable StorageVolume getStorageVolume(@NonNull File file) {
+        return getStorageVolume(getVolumeList(), file);
+    }
+
+    /**
+     * Return the {@link StorageVolume} that contains the given
+     * {@link MediaStore} item.
+     */
+    public @NonNull StorageVolume getStorageVolume(@NonNull Uri uri) {
+        String volumeName = MediaStore.getVolumeName(uri);
+
+        // When Uri is pointing at a synthetic volume, we're willing to query to
+        // resolve the actual volume name
+        if (Objects.equals(volumeName, MediaStore.VOLUME_EXTERNAL)) {
+            try (Cursor c = mContext.getContentResolver().query(uri,
+                    new String[] { MediaStore.MediaColumns.VOLUME_NAME }, null, null)) {
+                if (c.moveToFirst()) {
+                    volumeName = c.getString(0);
+                }
+            }
+        }
+
+        switch (volumeName) {
+            case MediaStore.VOLUME_EXTERNAL_PRIMARY:
+                return getPrimaryStorageVolume();
+            default:
+                for (StorageVolume vol : getStorageVolumes()) {
+                    if (Objects.equals(vol.getMediaStoreVolumeName(), volumeName)) {
+                        return vol;
+                    }
+                }
+        }
+        throw new IllegalStateException("Unknown volume for " + uri);
+    }
+
+    /** {@hide} */
+    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
+        return getStorageVolume(getVolumeList(userId, 0), file);
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
+        if (file == null) {
+            return null;
+        }
+        final String path = file.getAbsolutePath();
+        if (path.startsWith(DEPRECATE_DATA_PREFIX)) {
+            final Uri uri = ContentResolver.translateDeprecatedDataPath(path);
+            return AppGlobals.getInitialApplication().getSystemService(StorageManager.class)
+                    .getStorageVolume(uri);
+        }
+        try {
+            file = file.getCanonicalFile();
+        } catch (IOException ignored) {
+            Slog.d(TAG, "Could not get canonical path for " + file);
+            return null;
+        }
+        for (StorageVolume volume : volumes) {
+            File volumeFile = volume.getPathFile();
+            try {
+                volumeFile = volumeFile.getCanonicalFile();
+            } catch (IOException ignored) {
+                continue;
+            }
+            if (FileUtils.contains(volumeFile, file)) {
+                return volume;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets the state of a volume via its mountpoint.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public @NonNull String getVolumeState(String mountPoint) {
+        final StorageVolume vol = getStorageVolume(new File(mountPoint));
+        if (vol != null) {
+            return vol.getState();
+        } else {
+            return Environment.MEDIA_UNKNOWN;
+        }
+    }
+
+    /**
+     * Return the list of shared/external storage volumes currently available to
+     * the calling user.
+     * <p>
+     * These storage volumes are actively attached to the device, but may be in
+     * any mount state, as returned by {@link StorageVolume#getState()}. Returns
+     * both the primary shared storage device and any attached external volumes,
+     * including SD cards and USB drives.
+     */
+    public @NonNull List<StorageVolume> getStorageVolumes() {
+        final ArrayList<StorageVolume> res = new ArrayList<>();
+        Collections.addAll(res,
+                getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
+        return res;
+    }
+
+    /**
+     * Return the list of shared/external storage volumes currently available to
+     * the calling user and the user it shares media with. Please refer to
+     * <a href="https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support">
+     *     multi-user support</a> for more details.
+     *
+     * <p>
+     * This is similar to {@link StorageManager#getStorageVolumes()} except that the result also
+     * includes the volumes belonging to any user it shares media with
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+    public @NonNull List<StorageVolume> getStorageVolumesIncludingSharedProfiles() {
+        final ArrayList<StorageVolume> res = new ArrayList<>();
+        Collections.addAll(res,
+                getVolumeList(mContext.getUserId(),
+                        FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_SHARED_PROFILE));
+        return res;
+    }
+
+    /**
+     * Return the list of shared/external storage volumes both currently and
+     * recently available to the calling user.
+     * <p>
+     * Recently available storage volumes are likely to reappear in the future,
+     * so apps are encouraged to preserve any indexed metadata related to these
+     * volumes to optimize user experiences.
+     */
+    public @NonNull List<StorageVolume> getRecentStorageVolumes() {
+        final ArrayList<StorageVolume> res = new ArrayList<>();
+        Collections.addAll(res,
+                getVolumeList(mContext.getUserId(),
+                        FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_RECENT));
+        return res;
+    }
+
+    /**
+     * Return the primary shared/external storage volume available to the
+     * current user. This volume is the same storage device returned by
+     * {@link Environment#getExternalStorageDirectory()} and
+     * {@link Context#getExternalFilesDir(String)}.
+     */
+    public @NonNull StorageVolume getPrimaryStorageVolume() {
+        return getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
+    }
+
+    /** {@hide} */
+    public static Pair<String, Long> getPrimaryStoragePathAndSize() {
+        return Pair.create(null,
+                FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()
+                    + Environment.getRootDirectory().getTotalSpace()));
+    }
+
+    /** {@hide} */
+    public long getPrimaryStorageSize() {
+        return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()
+                + Environment.getRootDirectory().getTotalSpace());
+    }
+
+    /** {@hide} */
+    public void mkdirs(File file) {
+        BlockGuard.getVmPolicy().onPathAccess(file.getAbsolutePath());
+        try {
+            mStorageManager.mkdirs(mContext.getOpPackageName(), file.getAbsolutePath());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @removed */
+    public @NonNull StorageVolume[] getVolumeList() {
+        return getVolumeList(mContext.getUserId(), 0);
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) {
+        final IStorageManager storageManager = IStorageManager.Stub.asInterface(
+                ServiceManager.getService("mount"));
+        try {
+            String packageName = ActivityThread.currentOpPackageName();
+            if (packageName == null) {
+                // Package name can be null if the activity thread is running but the app
+                // hasn't bound yet. In this case we fall back to the first package in the
+                // current UID. This works for runtime permissions as permission state is
+                // per UID and permission realted app ops are updated for all UID packages.
+                String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
+                        android.os.Process.myUid());
+                if (packageNames == null || packageNames.length <= 0) {
+                    Log.w(TAG, "Missing package names; no storage volumes available");
+                    return new StorageVolume[0];
+                }
+                packageName = packageNames[0];
+            }
+            return storageManager.getVolumeList(userId, packageName, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns list of paths for all mountable volumes.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public @NonNull String[] getVolumePaths() {
+        StorageVolume[] volumes = getVolumeList();
+        int count = volumes.length;
+        String[] paths = new String[count];
+        for (int i = 0; i < count; i++) {
+            paths[i] = volumes[i].getPath();
+        }
+        return paths;
+    }
+
+    /** @removed */
+    public @NonNull StorageVolume getPrimaryVolume() {
+        return getPrimaryVolume(getVolumeList());
+    }
+
+    /** {@hide} */
+    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
+        for (StorageVolume volume : volumes) {
+            if (volume.isPrimary()) {
+                return volume;
+            }
+        }
+        throw new IllegalStateException("Missing primary storage");
+    }
+
+    /**
+     * Devices having above STORAGE_THRESHOLD_PERCENT_HIGH of total space free are considered to be
+     * in high free space category.
+     *
+     * @hide
+     */
+    public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+    /** {@hide} */
+    @TestApi
+    public static final String
+            STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high";
+    /**
+     * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be
+     * in low free space category and can be configured via
+     * Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE.
+     *
+     * @hide
+     */
+    public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW = 5;
+    /**
+     * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is
+     * allocated for cache.
+     *
+     * @hide
+     */
+    public static final int DEFAULT_CACHE_RESERVE_PERCENT_HIGH = 10;
+    /** {@hide} */
+    @TestApi
+    public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
+    /**
+     * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is
+     * allocated for cache.
+     *
+     * @hide
+     */
+    public static final int DEFAULT_CACHE_RESERVE_PERCENT_LOW = 2;
+    /** {@hide} */
+    @TestApi
+    public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low";
+
+    private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
+
+    private static final long DEFAULT_FULL_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(1);
+
+    /**
+     * Return the number of available bytes until the given path is considered
+     * running low on storage.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public long getStorageBytesUntilLow(File path) {
+        return path.getUsableSpace() - getStorageFullBytes(path);
+    }
+
+    /**
+     * Return the number of available bytes at which the given path is
+     * considered running low on storage.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public long getStorageLowBytes(File path) {
+        final long lowPercent = Settings.Global.getInt(mResolver,
+                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
+                DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW);
+        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
+
+        final long maxLowBytes = Settings.Global.getLong(mResolver,
+                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
+
+        return Math.min(lowBytes, maxLowBytes);
+    }
+
+    /**
+     * Compute the minimum number of bytes of storage on the device that could
+     * be reserved for cached data depending on the device state which is then passed on
+     * to getStorageCacheBytes.
+     *
+     * Input File path must point to a storage volume.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @SuppressLint("StreamFiles")
+    public long computeStorageCacheBytes(@NonNull File path) {
+        final int storageThresholdPercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                STORAGE_THRESHOLD_PERCENT_HIGH_KEY, DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
+        final int cacheReservePercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                CACHE_RESERVE_PERCENT_HIGH_KEY, DEFAULT_CACHE_RESERVE_PERCENT_HIGH);
+        final int cacheReservePercentLow = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                CACHE_RESERVE_PERCENT_LOW_KEY, DEFAULT_CACHE_RESERVE_PERCENT_LOW);
+        final long totalBytes = path.getTotalSpace();
+        final long usableBytes = path.getUsableSpace();
+        final long storageThresholdHighBytes = totalBytes * storageThresholdPercentHigh / 100;
+        final long storageThresholdLowBytes = getStorageLowBytes(path);
+        long result;
+        if (usableBytes > storageThresholdHighBytes) {
+            // If free space is >storageThresholdPercentHigh of total space,
+            // reserve cacheReservePercentHigh of total space
+            result = totalBytes * cacheReservePercentHigh / 100;
+        } else if (usableBytes < storageThresholdLowBytes) {
+            // If free space is <min(storageThresholdPercentLow of total space, 500MB),
+            // reserve cacheReservePercentLow of total space
+            result = totalBytes * cacheReservePercentLow / 100;
+        } else {
+            // Else, linearly interpolate the amount of space to reserve
+            double slope = (cacheReservePercentHigh - cacheReservePercentLow) * totalBytes
+                    / (100.0 * (storageThresholdHighBytes - storageThresholdLowBytes));
+            double intercept = totalBytes * cacheReservePercentLow / 100.0
+                    - storageThresholdLowBytes * slope;
+            result = Math.round(slope * usableBytes + intercept);
+        }
+        return result;
+    }
+
+    /**
+     * Return the minimum number of bytes of storage on the device that should
+     * be reserved for cached data.
+     *
+     * @hide
+     */
+    public long getStorageCacheBytes(@NonNull File path, @AllocateFlags int flags) {
+        if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
+            return 0;
+        } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED) != 0) {
+            return 0;
+        } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED) != 0) {
+            return computeStorageCacheBytes(path) / 2;
+        } else {
+            return computeStorageCacheBytes(path);
+        }
+    }
+
+    /**
+     * Return the number of available bytes at which the given path is
+     * considered full.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public long getStorageFullBytes(File path) {
+        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
+                DEFAULT_FULL_THRESHOLD_BYTES);
+    }
+
+    /** {@hide} */
+    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
+        try {
+            mStorageManager.createUserKey(userId, serialNumber, ephemeral);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void destroyUserKey(int userId) {
+        try {
+            mStorageManager.destroyUserKey(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void lockUserKey(int userId) {
+        try {
+            mStorageManager.lockUserKey(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
+        try {
+            mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+        try {
+            mStorageManager.destroyUserStorage(volumeUuid, userId, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @TestApi
+    public static boolean isUserKeyUnlocked(int userId) {
+        if (sStorageManager == null) {
+            sStorageManager = IStorageManager.Stub
+                    .asInterface(ServiceManager.getService("mount"));
+        }
+        if (sStorageManager == null) {
+            Slog.w(TAG, "Early during boot, assuming locked");
+            return false;
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return sStorageManager.isUserKeyUnlocked(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Return if data stored at or under the given path will be encrypted while
+     * at rest. This can help apps avoid the overhead of double-encrypting data.
+     */
+    public boolean isEncrypted(File file) {
+        if (FileUtils.contains(Environment.getDataDirectory(), file)) {
+            return isEncrypted();
+        } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
+            return true;
+        }
+        // TODO: extend to support shared storage
+        return false;
+    }
+
+    /** {@hide}
+     * Is this device encrypted?
+     * <p>
+     * Note: all devices launching with Android 10 (API level 29) or later are
+     * required to be encrypted.  This should only ever return false for
+     * in-development devices on which encryption has not yet been configured.
+     *
+     * @return true if encrypted, false if not encrypted
+     */
+    public static boolean isEncrypted() {
+        return RoSystemProperties.CRYPTO_ENCRYPTED;
+    }
+
+    /** {@hide}
+     * Does this device have file-based encryption (FBE) enabled?
+     * @return true if the device has file-based encryption enabled.
+     */
+    public static boolean isFileEncrypted() {
+        if (!isEncrypted()) {
+            return false;
+        }
+        return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
+    }
+
+    /** {@hide}
+     * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @Deprecated
+    public static boolean isFileEncryptedNativeOnly() {
+        return isFileEncrypted();
+    }
+
+    /** {@hide}
+     * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
+     */
+    @Deprecated
+    public static boolean isFileEncryptedNativeOrEmulated() {
+        return isFileEncrypted();
+    }
+
+    /** {@hide} */
+    public static boolean hasAdoptable() {
+        switch (SystemProperties.get(PROP_ADOPTABLE)) {
+            case "force_on":
+                return true;
+            case "force_off":
+                return false;
+            default:
+                return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
+        }
+    }
+
+    /**
+     * Return if the currently booted device has the "isolated storage" feature
+     * flag enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static boolean hasIsolatedStorage() {
+        return false;
+    }
+
+    /**
+     * @deprecated disabled now that FUSE has been replaced by sdcardfs
+     * @hide
+     */
+    @Deprecated
+    public static File maybeTranslateEmulatedPathToInternal(File path) {
+        // Disabled now that FUSE has been replaced by sdcardfs
+        return path;
+    }
+
+    /**
+     * Translate given shared storage path from a path in an app sandbox
+     * namespace to a path in the system namespace.
+     *
+     * @hide
+     */
+    public File translateAppToSystem(File file, int pid, int uid) {
+        return file;
+    }
+
+    /**
+     * Translate given shared storage path from a path in the system namespace
+     * to a path in an app sandbox namespace.
+     *
+     * @hide
+     */
+    public File translateSystemToApp(File file, int pid, int uid) {
+        return file;
+    }
+
+    /**
+     * Check that given app holds both permission and appop.
+     * @hide
+     */
+    public static boolean checkPermissionAndAppOp(Context context, boolean enforce, int pid,
+            int uid, String packageName, @NonNull String featureId, String permission, int op) {
+        return checkPermissionAndAppOp(context, enforce, pid, uid, packageName, featureId,
+                permission, op, true);
+    }
+
+    /**
+     * Check that given app holds both permission and appop but do not noteOp.
+     * @hide
+     */
+    public static boolean checkPermissionAndCheckOp(Context context, boolean enforce,
+            int pid, int uid, String packageName, String permission, int op) {
+        return checkPermissionAndAppOp(context, enforce, pid, uid, packageName,
+                null /* featureId is not needed when not noting */, permission, op, false);
+    }
+
+    /**
+     * Check that given app holds both permission and appop.
+     * @hide
+     */
+    private static boolean checkPermissionAndAppOp(Context context, boolean enforce, int pid,
+            int uid, String packageName, @Nullable String featureId, String permission, int op,
+            boolean note) {
+        if (context.checkPermission(permission, pid, uid) != PERMISSION_GRANTED) {
+            if (enforce) {
+                throw new SecurityException(
+                        "Permission " + permission + " denied for package " + packageName);
+            } else {
+                return false;
+            }
+        }
+
+        AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+        final int mode;
+        if (note) {
+            mode = appOps.noteOpNoThrow(op, uid, packageName, featureId, null);
+        } else {
+            try {
+                appOps.checkPackage(uid, packageName);
+            } catch (SecurityException e) {
+                if (enforce) {
+                    throw e;
+                } else {
+                    return false;
+                }
+            }
+            mode = appOps.checkOpNoThrow(op, uid, packageName);
+        }
+        switch (mode) {
+            case AppOpsManager.MODE_ALLOWED:
+                return true;
+            case AppOpsManager.MODE_DEFAULT:
+            case AppOpsManager.MODE_IGNORED:
+            case AppOpsManager.MODE_ERRORED:
+                if (enforce) {
+                    throw new SecurityException("Op " + AppOpsManager.opToName(op) + " "
+                            + AppOpsManager.modeToName(mode) + " for package " + packageName);
+                } else {
+                    return false;
+                }
+            default:
+                throw new IllegalStateException(
+                        AppOpsManager.opToName(op) + " has unknown mode "
+                                + AppOpsManager.modeToName(mode));
+        }
+    }
+
+    private boolean checkPermissionAndAppOp(boolean enforce, int pid, int uid, String packageName,
+            @Nullable String featureId, String permission, int op) {
+        return checkPermissionAndAppOp(mContext, enforce, pid, uid, packageName, featureId,
+                permission, op);
+    }
+
+    private boolean noteAppOpAllowingLegacy(boolean enforce,
+            int pid, int uid, String packageName, @Nullable String featureId, int op) {
+        final int mode = mAppOps.noteOpNoThrow(op, uid, packageName, featureId, null);
+        switch (mode) {
+            case AppOpsManager.MODE_ALLOWED:
+                return true;
+            case AppOpsManager.MODE_DEFAULT:
+            case AppOpsManager.MODE_IGNORED:
+            case AppOpsManager.MODE_ERRORED:
+                // Legacy apps technically have the access granted by this op,
+                // even when the op is denied
+                if ((mAppOps.checkOpNoThrow(OP_LEGACY_STORAGE, uid,
+                        packageName) == AppOpsManager.MODE_ALLOWED)) return true;
+
+                if (enforce) {
+                    throw new SecurityException("Op " + AppOpsManager.opToName(op) + " "
+                            + AppOpsManager.modeToName(mode) + " for package " + packageName);
+                } else {
+                    return false;
+                }
+            default:
+                throw new IllegalStateException(
+                        AppOpsManager.opToName(op) + " has unknown mode "
+                                + AppOpsManager.modeToName(mode));
+        }
+    }
+
+    // Callers must hold both the old and new permissions, so that we can
+    // handle obscure cases like when an app targets Q but was installed on
+    // a device that was originally running on P before being upgraded to Q.
+
+    /**
+     * @deprecated This method should not be used since it check slegacy permissions,
+     * no longer valid. Clients should check the appropriate permissions directly
+     * instead (e.g. READ_MEDIA_IMAGES).
+     *
+     * {@hide}
+     */
+    @Deprecated
+    public boolean checkPermissionReadImages(boolean enforce,
+            int pid, int uid, String packageName, @Nullable String featureId) {
+        if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+                READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
+            return false;
+        }
+        return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
+                OP_READ_MEDIA_IMAGES);
+    }
+
+    private boolean checkExternalStoragePermissionAndAppOp(boolean enforce,
+            int pid, int uid, String packageName, @Nullable String featureId, String permission,
+            int op) {
+        // First check if app has MANAGE_EXTERNAL_STORAGE.
+        final int mode = mAppOps.noteOpNoThrow(OP_MANAGE_EXTERNAL_STORAGE, uid, packageName,
+                featureId, null);
+        if (mode == AppOpsManager.MODE_ALLOWED) {
+            return true;
+        }
+        if (mode == AppOpsManager.MODE_DEFAULT && mContext.checkPermission(
+                  MANAGE_EXTERNAL_STORAGE, pid, uid) == PERMISSION_GRANTED) {
+            return true;
+        }
+        // If app doesn't have MANAGE_EXTERNAL_STORAGE, then check if it has requested granular
+        // permission.
+        return checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, permission, op);
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory)
+                    throws IOException {
+        Preconditions.checkNotNull(callback);
+        MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1);
+        // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
+        // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
+        // the bridge by calling mountProxyFileDescriptorBridge.
+        while (true) {
+            try {
+                synchronized (mFuseAppLoopLock) {
+                    boolean newlyCreated = false;
+                    if (mFuseAppLoop == null) {
+                        final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge();
+                        if (mount == null) {
+                            throw new IOException("Failed to mount proxy bridge");
+                        }
+                        mFuseAppLoop = new FuseAppLoop(mount.mountPointId, mount.fd, factory);
+                        newlyCreated = true;
+                    }
+                    if (handler == null) {
+                        handler = new Handler(Looper.getMainLooper());
+                    }
+                    try {
+                        final int fileId = mFuseAppLoop.registerCallback(callback, handler);
+                        final ParcelFileDescriptor pfd = mStorageManager.openProxyFileDescriptor(
+                                mFuseAppLoop.getMountPointId(), fileId, mode);
+                        if (pfd == null) {
+                            mFuseAppLoop.unregisterCallback(fileId);
+                            throw new FuseUnavailableMountException(
+                                    mFuseAppLoop.getMountPointId());
+                        }
+                        return pfd;
+                    } catch (FuseUnavailableMountException exception) {
+                        // The bridge is being unmounted. Tried to recreate it unless the bridge was
+                        // just created.
+                        if (newlyCreated) {
+                            throw new IOException(exception);
+                        }
+                        mFuseAppLoop = null;
+                        continue;
+                    }
+                }
+            } catch (RemoteException e) {
+                // Cannot recover from remote exception.
+                throw new IOException(e);
+            }
+        }
+    }
+
+    /** {@hide} */
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback)
+                    throws IOException {
+        return openProxyFileDescriptor(mode, callback, null, null);
+    }
+
+    /**
+     * Opens a seekable {@link ParcelFileDescriptor} that proxies all low-level
+     * I/O requests back to the given {@link ProxyFileDescriptorCallback}.
+     * <p>
+     * This can be useful when you want to provide quick access to a large file
+     * that isn't backed by a real file on disk, such as a file on a network
+     * share, cloud storage service, etc. As an example, you could respond to a
+     * {@link ContentResolver#openFileDescriptor(android.net.Uri, String)}
+     * request by returning a {@link ParcelFileDescriptor} created with this
+     * method, and then stream the content on-demand as requested.
+     * <p>
+     * Another useful example might be where you have an encrypted file that
+     * you're willing to decrypt on-demand, but where you want to avoid
+     * persisting the cleartext version.
+     *
+     * @param mode The desired access mode, must be one of
+     *            {@link ParcelFileDescriptor#MODE_READ_ONLY},
+     *            {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
+     *            {@link ParcelFileDescriptor#MODE_READ_WRITE}
+     * @param callback Callback to process file operation requests issued on
+     *            returned file descriptor.
+     * @param handler Handler that invokes callback methods.
+     * @return Seekable ParcelFileDescriptor.
+     * @throws IOException
+     */
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback, Handler handler)
+                    throws IOException {
+        Preconditions.checkNotNull(handler);
+        return openProxyFileDescriptor(mode, callback, handler, null);
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public int getProxyFileDescriptorMountPointId() {
+        synchronized (mFuseAppLoopLock) {
+            return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1;
+        }
+    }
+
+    /**
+     * Return quota size in bytes for all cached data belonging to the calling
+     * app on the given storage volume.
+     * <p>
+     * If your app goes above this quota, your cached files will be some of the
+     * first to be deleted when additional disk space is needed. Conversely, if
+     * your app stays under this quota, your cached files will be some of the
+     * last to be deleted when additional disk space is needed.
+     * <p>
+     * This quota will change over time depending on how frequently the user
+     * interacts with your app, and depending on how much system-wide disk space
+     * is used.
+     * <p class="note">
+     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
+     * then cached data for all packages in your shared UID is tracked together
+     * as a single unit.
+     * </p>
+     *
+     * @param storageUuid the UUID of the storage volume that you're interested
+     *            in. The UUID for a specific path can be obtained using
+     *            {@link #getUuidForPath(File)}.
+     * @throws IOException when the storage device isn't present, or when it
+     *             doesn't support cache quotas.
+     * @see #getCacheSizeBytes(UUID)
+     */
+    @WorkerThread
+    public @BytesLong long getCacheQuotaBytes(@NonNull UUID storageUuid) throws IOException {
+        try {
+            final ApplicationInfo app = mContext.getApplicationInfo();
+            return mStorageManager.getCacheQuotaBytes(convert(storageUuid), app.uid);
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return total size in bytes of all cached data belonging to the calling
+     * app on the given storage volume.
+     * <p>
+     * Cached data tracked by this method always includes
+     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
+     * it also includes {@link Context#getExternalCacheDir()} if the primary
+     * shared/external storage is hosted on the same storage device as your
+     * private data.
+     * <p class="note">
+     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
+     * then cached data for all packages in your shared UID is tracked together
+     * as a single unit.
+     * </p>
+     *
+     * @param storageUuid the UUID of the storage volume that you're interested
+     *            in. The UUID for a specific path can be obtained using
+     *            {@link #getUuidForPath(File)}.
+     * @throws IOException when the storage device isn't present, or when it
+     *             doesn't support cache quotas.
+     * @see #getCacheQuotaBytes(UUID)
+     */
+    @WorkerThread
+    public @BytesLong long getCacheSizeBytes(@NonNull UUID storageUuid) throws IOException {
+        try {
+            final ApplicationInfo app = mContext.getApplicationInfo();
+            return mStorageManager.getCacheSizeBytes(convert(storageUuid), app.uid);
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /** @hide */
+    @IntDef(prefix = { "MOUNT_MODE_" }, value = {
+            MOUNT_MODE_EXTERNAL_NONE,
+            MOUNT_MODE_EXTERNAL_DEFAULT,
+            MOUNT_MODE_EXTERNAL_INSTALLER,
+            MOUNT_MODE_EXTERNAL_PASS_THROUGH,
+            MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE
+    })
+    /** @hide */
+    public @interface MountMode {}
+
+    /**
+     * No external storage should be mounted.
+     * @hide
+     */
+    @SystemApi
+    public static final int MOUNT_MODE_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
+    /**
+     * Default external storage should be mounted.
+     * @hide
+     */
+    @SystemApi
+    public static final int MOUNT_MODE_EXTERNAL_DEFAULT = IVold.REMOUNT_MODE_DEFAULT;
+    /**
+     * Mount mode for package installers which should give them access to
+     * all obb dirs in addition to their package sandboxes
+     * @hide
+     */
+    @SystemApi
+    public static final int MOUNT_MODE_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
+    /**
+     * The lower file system should be bind mounted directly on external storage
+     * @hide
+     */
+    @SystemApi
+    public static final int MOUNT_MODE_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH;
+
+    /**
+     * Use the regular scoped storage filesystem, but Android/ should be writable.
+     * Used to support the applications hosting DownloadManager and the MTP server.
+     * @hide
+     */
+    @SystemApi
+    public static final int MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE =
+            IVold.REMOUNT_MODE_ANDROID_WRITABLE;
+    /**
+     * Flag indicating that a disk space allocation request should operate in an
+     * aggressive mode. This flag should only be rarely used in situations that
+     * are critical to system health or security.
+     * <p>
+     * When set, the system is more aggressive about the data that it considers
+     * for possible deletion when allocating disk space.
+     * <p class="note">
+     * Note: your app must hold the
+     * {@link android.Manifest.permission#ALLOCATE_AGGRESSIVE} permission for
+     * this flag to take effect.
+     * </p>
+     *
+     * @see #getAllocatableBytes(UUID, int)
+     * @see #allocateBytes(UUID, long, int)
+     * @see #allocateBytes(FileDescriptor, long, int)
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE)
+    @SystemApi
+    public static final int FLAG_ALLOCATE_AGGRESSIVE = 1 << 0;
+
+    /**
+     * Flag indicating that a disk space allocation request should be allowed to
+     * clear up to all reserved disk space.
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOCATE_DEFY_ALL_RESERVED = 1 << 1;
+
+    /**
+     * Flag indicating that a disk space allocation request should be allowed to
+     * clear up to half of all reserved disk space.
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOCATE_DEFY_HALF_RESERVED = 1 << 2;
+
+    /**
+     * Flag indicating that a disk space check should not take into account
+     * freeable cached space when determining allocatable space.
+     *
+     * Intended for use with {@link #getAllocatableBytes()}.
+     * @hide
+     */
+    public static final int FLAG_ALLOCATE_NON_CACHE_ONLY = 1 << 3;
+
+    /**
+     * Flag indicating that a disk space check should only return freeable
+     * cached space when determining allocatable space.
+     *
+     * Intended for use with {@link #getAllocatableBytes()}.
+     * @hide
+     */
+    public static final int FLAG_ALLOCATE_CACHE_ONLY = 1 << 4;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_ALLOCATE_" }, value = {
+            FLAG_ALLOCATE_AGGRESSIVE,
+            FLAG_ALLOCATE_DEFY_ALL_RESERVED,
+            FLAG_ALLOCATE_DEFY_HALF_RESERVED,
+            FLAG_ALLOCATE_NON_CACHE_ONLY,
+            FLAG_ALLOCATE_CACHE_ONLY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AllocateFlags {}
+
+    /**
+     * Return the maximum number of new bytes that your app can allocate for
+     * itself on the given storage volume. This value is typically larger than
+     * {@link File#getUsableSpace()}, since the system may be willing to delete
+     * cached files to satisfy an allocation request. You can then allocate
+     * space for yourself using {@link #allocateBytes(UUID, long)} or
+     * {@link #allocateBytes(FileDescriptor, long)}.
+     * <p>
+     * This method is best used as a pre-flight check, such as deciding if there
+     * is enough space to store an entire music album before you allocate space
+     * for each audio file in the album. Attempts to allocate disk space beyond
+     * the returned value will fail.
+     * <p>
+     * If the returned value is not large enough for the data you'd like to
+     * persist, you can launch {@link #ACTION_MANAGE_STORAGE} with the
+     * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help
+     * involve the user in freeing up disk space.
+     * <p>
+     * If you're progressively allocating an unbounded amount of storage space
+     * (such as when recording a video) you should avoid calling this method
+     * more than once every 30 seconds.
+     * <p class="note">
+     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
+     * then allocatable space for all packages in your shared UID is tracked
+     * together as a single unit.
+     * </p>
+     *
+     * @param storageUuid the UUID of the storage volume where you're
+     *            considering allocating disk space, since allocatable space can
+     *            vary widely depending on the underlying storage device. The
+     *            UUID for a specific path can be obtained using
+     *            {@link #getUuidForPath(File)}.
+     * @return the maximum number of new bytes that the calling app can allocate
+     *         using {@link #allocateBytes(UUID, long)} or
+     *         {@link #allocateBytes(FileDescriptor, long)}.
+     * @throws IOException when the storage device isn't present, or when it
+     *             doesn't support allocating space.
+     */
+    @WorkerThread
+    public @BytesLong long getAllocatableBytes(@NonNull UUID storageUuid)
+            throws IOException {
+        return getAllocatableBytes(storageUuid, 0);
+    }
+
+    /** @hide */
+    @SystemApi
+    @WorkerThread
+    @SuppressLint("RequiresPermission")
+    public long getAllocatableBytes(@NonNull UUID storageUuid,
+            @RequiresPermission @AllocateFlags int flags) throws IOException {
+        try {
+            return mStorageManager.getAllocatableBytes(convert(storageUuid), flags,
+                    mContext.getOpPackageName());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allocate the requested number of bytes for your application to use on the
+     * given storage volume. This will cause the system to delete any cached
+     * files necessary to satisfy your request.
+     * <p>
+     * Attempts to allocate disk space beyond the value returned by
+     * {@link #getAllocatableBytes(UUID)} will fail.
+     * <p>
+     * Since multiple apps can be running simultaneously, this method may be
+     * subject to race conditions. If possible, consider using
+     * {@link #allocateBytes(FileDescriptor, long)} which will guarantee
+     * that bytes are allocated to an opened file.
+     * <p>
+     * If you're progressively allocating an unbounded amount of storage space
+     * (such as when recording a video) you should avoid calling this method
+     * more than once every 60 seconds.
+     *
+     * @param storageUuid the UUID of the storage volume where you'd like to
+     *            allocate disk space. The UUID for a specific path can be
+     *            obtained using {@link #getUuidForPath(File)}.
+     * @param bytes the number of bytes to allocate.
+     * @throws IOException when the storage device isn't present, or when it
+     *             doesn't support allocating space, or if the device had
+     *             trouble allocating the requested space.
+     * @see #getAllocatableBytes(UUID)
+     */
+    @WorkerThread
+    public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes)
+            throws IOException {
+        allocateBytes(storageUuid, bytes, 0);
+    }
+
+    /** @hide */
+    @SystemApi
+    @WorkerThread
+    @SuppressLint("RequiresPermission")
+    public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes,
+            @RequiresPermission @AllocateFlags int flags) throws IOException {
+        try {
+            mStorageManager.allocateBytes(convert(storageUuid), bytes, flags,
+                    mContext.getOpPackageName());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the External Storage mount mode corresponding to the given uid and packageName.
+     * These mount modes specify different views and access levels for
+     * different apps on external storage.
+     *
+     * @params uid UID of the application
+     * @params packageName name of the package
+     * @return {@code MountMode} for the given uid and packageName.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE)
+    @SystemApi
+    @MountMode
+    public int getExternalStorageMountMode(int uid, @NonNull String packageName) {
+        try {
+            return mStorageManager.getExternalStorageMountMode(uid, packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allocate the requested number of bytes for your application to use in the
+     * given open file. This will cause the system to delete any cached files
+     * necessary to satisfy your request.
+     * <p>
+     * Attempts to allocate disk space beyond the value returned by
+     * {@link #getAllocatableBytes(UUID)} will fail.
+     * <p>
+     * This method guarantees that bytes have been allocated to the opened file,
+     * otherwise it will throw if fast allocation is not possible. Fast
+     * allocation is typically only supported in private app data directories,
+     * and on shared/external storage devices which are emulated.
+     * <p>
+     * If you're progressively allocating an unbounded amount of storage space
+     * (such as when recording a video) you should avoid calling this method
+     * more than once every 60 seconds.
+     *
+     * @param fd the open file that you'd like to allocate disk space for.
+     * @param bytes the number of bytes to allocate. This is the desired final
+     *            size of the open file. If the open file is smaller than this
+     *            requested size, it will be extended without modifying any
+     *            existing contents. If the open file is larger than this
+     *            requested size, it will be truncated.
+     * @throws IOException when the storage device isn't present, or when it
+     *             doesn't support allocating space, or if the device had
+     *             trouble allocating the requested space.
+     * @see #isAllocationSupported(FileDescriptor)
+     * @see Environment#isExternalStorageEmulated(File)
+     */
+    @WorkerThread
+    public void allocateBytes(FileDescriptor fd, @BytesLong long bytes) throws IOException {
+        allocateBytes(fd, bytes, 0);
+    }
+
+    /** @hide */
+    @SystemApi
+    @WorkerThread
+    @SuppressLint("RequiresPermission")
+    public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
+            @RequiresPermission @AllocateFlags int flags) throws IOException {
+        final File file = ParcelFileDescriptor.getFile(fd);
+        final UUID uuid = getUuidForPath(file);
+        for (int i = 0; i < 3; i++) {
+            try {
+                final long haveBytes = Os.fstat(fd).st_blocks * 512;
+                final long needBytes = bytes - haveBytes;
+
+                if (needBytes > 0) {
+                    allocateBytes(uuid, needBytes, flags);
+                }
+
+                try {
+                    Os.posix_fallocate(fd, 0, bytes);
+                    return;
+                } catch (ErrnoException e) {
+                    if (e.errno == OsConstants.ENOSYS || e.errno == OsConstants.ENOTSUP) {
+                        Log.w(TAG, "fallocate() not supported; falling back to ftruncate()");
+                        Os.ftruncate(fd, bytes);
+                        return;
+                    } else {
+                        throw e;
+                    }
+                }
+            } catch (ErrnoException e) {
+                if (e.errno == OsConstants.ENOSPC) {
+                    Log.w(TAG, "Odd, not enough space; let's try again?");
+                    continue;
+                }
+                throw e.rethrowAsIOException();
+            }
+        }
+        throw new IOException(
+                "Well this is embarassing; we can't allocate " + bytes + " for " + file);
+    }
+
+    private static final String XATTR_CACHE_GROUP = "user.cache_group";
+    private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone";
+
+
+    // Project IDs below must match android_projectid_config.h
+    /**
+     * Default project ID for files on external storage
+     *
+     * {@hide}
+     */
+    public static final int PROJECT_ID_EXT_DEFAULT = 1000;
+
+    /**
+     * project ID for audio files on external storage
+     *
+     * {@hide}
+     */
+    public static final int PROJECT_ID_EXT_MEDIA_AUDIO = 1001;
+
+    /**
+     * project ID for video files on external storage
+     *
+     * {@hide}
+     */
+    public static final int PROJECT_ID_EXT_MEDIA_VIDEO = 1002;
+
+    /**
+     * project ID for image files on external storage
+     *
+     * {@hide}
+     */
+    public static final int PROJECT_ID_EXT_MEDIA_IMAGE = 1003;
+
+    /**
+     * Constant for use with
+     * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+     * is not a media file.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int QUOTA_TYPE_MEDIA_NONE = 0;
+
+    /**
+     * Constant for use with
+     * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+     * is an image file.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int QUOTA_TYPE_MEDIA_IMAGE = 1;
+
+    /**
+     * Constant for use with
+     * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+     * is an audio file.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int QUOTA_TYPE_MEDIA_AUDIO = 2;
+
+    /**
+     * Constant for use with
+     * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+     * is a video file.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int QUOTA_TYPE_MEDIA_VIDEO = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "QUOTA_TYPE_" }, value = {
+            QUOTA_TYPE_MEDIA_NONE,
+            QUOTA_TYPE_MEDIA_AUDIO,
+            QUOTA_TYPE_MEDIA_VIDEO,
+            QUOTA_TYPE_MEDIA_IMAGE,
+    })
+    public @interface QuotaType {}
+
+    private static native boolean setQuotaProjectId(String path, long projectId);
+
+    private static long getProjectIdForUser(int userId, int projectId) {
+        // Much like UserHandle.getUid(), store the user ID in the upper bits
+        return userId * PER_USER_RANGE + projectId;
+    }
+
+    /**
+     * Let StorageManager know that the quota type for a file on external storage should
+     * be updated. Android tracks quotas for various media types. Consequently, this should be
+     * called on first creation of a new file on external storage, and whenever the
+     * media type of the file is updated later.
+     *
+     * This API doesn't require any special permissions, though typical implementations
+     * will require being called from an SELinux domain that allows setting file attributes
+     * related to quota (eg the GID or project ID).
+     * If the calling user has MANAGE_EXTERNAL_STORAGE permissions, quota for shared profile's
+     * volumes is also updated.
+     *
+     * The default platform user of this API is the MediaProvider process, which is
+     * responsible for managing all of external storage.
+     *
+     * @param path the path to the file for which we should update the quota type
+     * @param quotaType the quota type of the file; this is based on the
+     *                  {@code QuotaType} constants, eg
+     *                  {@code StorageManager.QUOTA_TYPE_MEDIA_AUDIO}
+     *
+     * @throws IllegalArgumentException if {@code quotaType} does not correspond to a valid
+     *                                  quota type.
+     * @throws IOException              if the quota type could not be updated.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void updateExternalStorageFileQuotaType(@NonNull File path,
+            @QuotaType int quotaType) throws IOException {
+        long projectId;
+        final String filePath = path.getCanonicalPath();
+        int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
+        // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are also
+        // returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
+        if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
+            volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
+        }
+        final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
+        final StorageVolume volume = getStorageVolume(availableVolumes, path);
+        if (volume == null) {
+            Log.w(TAG, "Failed to update quota type for " + filePath);
+            return;
+        }
+        if (!volume.isEmulated()) {
+            // We only support quota tracking on emulated filesystems
+            return;
+        }
+
+        final int userId = volume.getOwner().getIdentifier();
+        if (userId < 0) {
+            throw new IllegalStateException("Failed to update quota type for " + filePath);
+        }
+        switch (quotaType) {
+            case QUOTA_TYPE_MEDIA_NONE:
+                projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_DEFAULT);
+                break;
+            case QUOTA_TYPE_MEDIA_AUDIO:
+                projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_AUDIO);
+                break;
+            case QUOTA_TYPE_MEDIA_VIDEO:
+                projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_VIDEO);
+                break;
+            case QUOTA_TYPE_MEDIA_IMAGE:
+                projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_IMAGE);
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid quota type: " + quotaType);
+        }
+        if (!setQuotaProjectId(filePath, projectId)) {
+            throw new IOException("Failed to update quota type for " + filePath);
+        }
+    }
+
+    /**
+     * Asks StorageManager to fixup the permissions of an application-private directory.
+     *
+     * On devices without sdcardfs, filesystem permissions aren't magically fixed up. This
+     * is problematic mostly in application-private directories, which are owned by the
+     * application itself; if another process with elevated permissions creates a file
+     * in these directories, the UID will be wrong, and the owning package won't be able
+     * to access the files.
+     *
+     * This API can be used to recursively fix up the permissions on the passed in path.
+     * The default platform user of this API is the DownloadProvider, which can download
+     * things in application-private directories on their behalf.
+     *
+     * This API doesn't require any special permissions, because it merely changes the
+     * permissions of a directory to what they should anyway be.
+     *
+     * @param path the path for which we should fix up the permissions
+     *
+     * @hide
+     */
+    public void fixupAppDir(@NonNull File path) {
+        try {
+            mStorageManager.fixupAppDir(path.getCanonicalPath());
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to get canonical path for " + path.getPath(), e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    private static void setCacheBehavior(File path, String name, boolean enabled)
+            throws IOException {
+        if (!path.isDirectory()) {
+            throw new IOException("Cache behavior can only be set on directories");
+        }
+        if (enabled) {
+            try {
+                Os.setxattr(path.getAbsolutePath(), name,
+                        "1".getBytes(StandardCharsets.UTF_8), 0);
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        } else {
+            try {
+                Os.removexattr(path.getAbsolutePath(), name);
+            } catch (ErrnoException e) {
+                if (e.errno != OsConstants.ENODATA) {
+                    throw e.rethrowAsIOException();
+                }
+            }
+        }
+    }
+
+    /** {@hide} */
+    private static boolean isCacheBehavior(File path, String name) throws IOException {
+        try {
+            Os.getxattr(path.getAbsolutePath(), name);
+            return true;
+        } catch (ErrnoException e) {
+            if (e.errno != OsConstants.ENODATA) {
+                throw e.rethrowAsIOException();
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Enable or disable special cache behavior that treats this directory and
+     * its contents as an entire group.
+     * <p>
+     * When enabled and this directory is considered for automatic deletion by
+     * the OS, all contained files will either be deleted together, or not at
+     * all. This is useful when you have a directory that contains several
+     * related metadata files that depend on each other, such as movie file and
+     * a subtitle file.
+     * <p>
+     * When enabled, the <em>newest</em> {@link File#lastModified()} value of
+     * any contained files is considered the modified time of the entire
+     * directory.
+     * <p>
+     * This behavior can only be set on a directory, and it applies recursively
+     * to all contained files and directories.
+     */
+    public void setCacheBehaviorGroup(File path, boolean group) throws IOException {
+        setCacheBehavior(path, XATTR_CACHE_GROUP, group);
+    }
+
+    /**
+     * Read the current value set by
+     * {@link #setCacheBehaviorGroup(File, boolean)}.
+     */
+    public boolean isCacheBehaviorGroup(File path) throws IOException {
+        return isCacheBehavior(path, XATTR_CACHE_GROUP);
+    }
+
+    /**
+     * Enable or disable special cache behavior that leaves deleted cache files
+     * intact as tombstones.
+     * <p>
+     * When enabled and a file contained in this directory is automatically
+     * deleted by the OS, the file will be truncated to have a length of 0 bytes
+     * instead of being fully deleted. This is useful if you need to distinguish
+     * between a file that was deleted versus one that never existed.
+     * <p>
+     * This behavior can only be set on a directory, and it applies recursively
+     * to all contained files and directories.
+     * <p class="note">
+     * Note: this behavior is ignored completely if the user explicitly requests
+     * that all cached data be cleared.
+     * </p>
+     */
+    public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException {
+        setCacheBehavior(path, XATTR_CACHE_TOMBSTONE, tombstone);
+    }
+
+    /**
+     * Read the current value set by
+     * {@link #setCacheBehaviorTombstone(File, boolean)}.
+     */
+    public boolean isCacheBehaviorTombstone(File path) throws IOException {
+        return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE);
+    }
+
+    /**
+     * Returns true if {@code uuid} is a FAT volume identifier. FAT Volume identifiers
+     * are 32 randomly generated bits that are represented in string form as AAAA-AAAA.
+     */
+    private static boolean isFatVolumeIdentifier(String uuid) {
+        return uuid.length() == 9 && uuid.charAt(4) == '-';
+    }
+
+    /** {@hide} */
+    @TestApi
+    public static @NonNull UUID convert(@Nullable String uuid) {
+        // UUID_PRIVATE_INTERNAL is null, so this accepts nullable input
+        if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) {
+            return UUID_DEFAULT;
+        } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) {
+            return UUID_PRIMARY_PHYSICAL_;
+        } else if (Objects.equals(uuid, UUID_SYSTEM)) {
+            return UUID_SYSTEM_;
+        } else if (isFatVolumeIdentifier(uuid)) {
+            // FAT volume identifiers are not UUIDs but we need to coerce them into
+            // UUIDs in order to satisfy apis that take java.util.UUID arguments.
+            //
+            // We coerce a 32 bit fat volume identifier of the form XXXX-YYYY into
+            // a UUID of form "fafafafa-fafa-5afa-8afa-fafaXXXXYYYY". This is an
+            // RFC-422 UUID with Version 5, which is a namespaced UUID. The UUIDs we
+            // coerce into are not true namespace UUIDs; although FAT storage volume
+            // identifiers are unique names within a fixed namespace, this UUID is not
+            // based on an SHA-1 hash of the name. We avoid the SHA-1 hash because
+            // (a) we need this transform to be reversible (b) it's pointless to generate
+            // a 128 bit hash from a 32 bit value.
+            return UUID.fromString(FAT_UUID_PREFIX + uuid.replace("-", ""));
+        } else {
+            return UUID.fromString(uuid);
+        }
+    }
+
+    /** {@hide} */
+    @TestApi
+    public static @NonNull String convert(@NonNull UUID storageUuid) {
+        if (UUID_DEFAULT.equals(storageUuid)) {
+            return UUID_PRIVATE_INTERNAL;
+        } else if (UUID_PRIMARY_PHYSICAL_.equals(storageUuid)) {
+            return UUID_PRIMARY_PHYSICAL;
+        } else if (UUID_SYSTEM_.equals(storageUuid)) {
+            return UUID_SYSTEM;
+        } else {
+            String uuidString = storageUuid.toString();
+            // This prefix match will exclude fsUuids from private volumes because
+            // (a) linux fsUuids are generally Version 4 (random) UUIDs so the prefix
+            // will contain 4xxx instead of 5xxx and (b) we've already matched against
+            // known namespace (Version 5) UUIDs above.
+            if (uuidString.startsWith(FAT_UUID_PREFIX)) {
+                String fatStr = uuidString.substring(FAT_UUID_PREFIX.length())
+                        .toUpperCase(Locale.US);
+                return fatStr.substring(0, 4) + "-" + fatStr.substring(4);
+            }
+
+            return storageUuid.toString();
+        }
+    }
+
+    /**
+     * Check whether the device supports filesystem checkpoint.
+     *
+     * @return true if the device supports filesystem checkpoint, false otherwise.
+     */
+    public boolean isCheckpointSupported() {
+        try {
+            return mStorageManager.supportsCheckpoint();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Reason to provide if app IO is blocked/resumed for unknown reasons
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0;
+
+    /**
+     * Reason to provide if app IO is blocked/resumed because of transcoding
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1;
+
+    /**
+     * Constants for use with
+     * {@link #notifyAppIoBlocked} and {@link notifyAppIoResumed}, to specify the reason an app's
+     * IO is blocked/resumed.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "APP_IO_BLOCKED_REASON_" }, value = {
+                APP_IO_BLOCKED_REASON_TRANSCODING,
+                APP_IO_BLOCKED_REASON_UNKNOWN,
+    })
+    public @interface AppIoBlockedReason {}
+
+    /**
+     * Notify the system that an app with {@code uid} and {@code tid} is blocked on an IO request on
+     * {@code volumeUuid} for {@code reason}.
+     *
+     * This blocked state can be used to modify the ANR behavior for the app while it's blocked.
+     * For example during transcoding.
+     *
+     * This can only be called by the {@link ExternalStorageService} holding the
+     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission.
+     *
+     * @param volumeUuid the UUID of the storage volume that the app IO is blocked on
+     * @param uid the UID of the app blocked on IO
+     * @param tid the tid of the app blocked on IO
+     * @param reason the reason the app is blocked on IO
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void notifyAppIoBlocked(@NonNull UUID volumeUuid, int uid, int tid,
+            @AppIoBlockedReason int reason) {
+        Objects.requireNonNull(volumeUuid);
+        try {
+            mStorageManager.notifyAppIoBlocked(convert(volumeUuid), uid, tid, reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify the system that an app with {@code uid} and {@code tid} has resmued a previously
+     * blocked IO request on {@code volumeUuid} for {@code reason}.
+     *
+     * All app IO will be automatically marked as unblocked if {@code volumeUuid} is unmounted.
+     *
+     * This can only be called by the {@link ExternalStorageService} holding the
+     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission.
+     *
+     * @param volumeUuid the UUID of the storage volume that the app IO is resumed on
+     * @param uid the UID of the app resuming IO
+     * @param tid the tid of the app resuming IO
+     * @param reason the reason the app is resuming IO
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void notifyAppIoResumed(@NonNull UUID volumeUuid, int uid, int tid,
+            @AppIoBlockedReason int reason) {
+        Objects.requireNonNull(volumeUuid);
+        try {
+            mStorageManager.notifyAppIoResumed(convert(volumeUuid), uid, tid, reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check if {@code uid} with {@code tid} is blocked on IO for {@code reason}.
+     *
+     * This requires {@link ExternalStorageService} the
+     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission.
+     *
+     * @param volumeUuid the UUID of the storage volume to check IO blocked status
+     * @param uid the UID of the app to check IO blocked status
+     * @param tid the tid of the app to check IO blocked status
+     * @param reason the reason to check IO blocked status for
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean isAppIoBlocked(@NonNull UUID volumeUuid, int uid, int tid,
+            @AppIoBlockedReason int reason) {
+        Objects.requireNonNull(volumeUuid);
+        try {
+            return mStorageManager.isAppIoBlocked(convert(volumeUuid), uid, tid, reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify the system of the current cloud media provider.
+     *
+     * This can only be called by the {@link android.service.storage.ExternalStorageService}
+     * holding the {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission.
+     *
+     * @param authority the authority of the content provider
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void setCloudMediaProvider(@Nullable String authority) {
+        try {
+            mStorageManager.setCloudMediaProvider(authority);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the authority of the current cloud media provider that was set by the
+     * {@link android.service.storage.ExternalStorageService} holding the
+     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission via
+     * {@link #setCloudMediaProvider(String)}.
+     *
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public String getCloudMediaProvider() {
+        try {
+            return mStorageManager.getCloudMediaProvider();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private final Object mFuseAppLoopLock = new Object();
+
+    @GuardedBy("mFuseAppLoopLock")
+    private @Nullable FuseAppLoop mFuseAppLoop = null;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int CRYPT_TYPE_PASSWORD = 0;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int CRYPT_TYPE_DEFAULT = 1;
+}
diff --git a/android-34/android/os/storage/StorageManagerInternal.java b/android-34/android/os/storage/StorageManagerInternal.java
new file mode 100644
index 0000000..059bd84
--- /dev/null
+++ b/android-34/android/os/storage/StorageManagerInternal.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 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.os.storage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.IVold;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Mount service local interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class StorageManagerInternal {
+    /**
+     * Gets the mount mode to use for a given UID
+     *
+     * @param uid The UID for which to get mount mode.
+     * @param packageName The package in the UID for making the call.
+     * @return The mount mode.
+     */
+    public abstract int getExternalStorageMountMode(int uid, String packageName);
+
+    /**
+     * Checks whether the {@code packageName} with {@code uid} has full external storage access via
+     * the {@link MANAGE_EXTERNAL_STORAGE} permission.
+     *
+     * @param uid the UID for which to check access.
+     * @param packageName the package in the UID for making the call.
+     * @return whether the {@code packageName} has full external storage access.
+     * Returns {@code true} if it has access, {@code false} otherwise.
+     */
+    public abstract boolean hasExternalStorageAccess(int uid, String packageName);
+
+    /**
+     * A listener for reset events in the StorageManagerService.
+     */
+    public interface ResetListener {
+        /**
+         * A method that should be triggered internally by StorageManagerInternal
+         * when StorageManagerService reset happens.
+         *
+         * @param vold The binder object to vold.
+         */
+        void onReset(IVold vold);
+    }
+
+    /**
+     * Return true if fuse is mounted.
+     */
+    public abstract boolean isFuseMounted(int userId);
+
+    /**
+     * Create storage directories if it does not exist.
+     * Return true if the directories were setup correctly, otherwise false.
+     */
+    public abstract boolean prepareStorageDirs(int userId, Set<String> packageList,
+            String processName);
+
+    /**
+     * Add a listener to listen to reset event in StorageManagerService.
+     *
+     * @param listener The listener that will be notified on reset events.
+     */
+    public abstract void addResetListener(ResetListener listener);
+
+    /**
+     * Notified when any app op changes so that storage mount points can be updated if the app op
+     * affects them.
+     */
+    public abstract void onAppOpsChanged(int code, int uid,
+            @Nullable String packageName, int mode, int previousMode);
+
+    /**
+     * Asks the StorageManager to reset all state for the provided user; this will result
+     * in the unmounting for all volumes of the user, and, if the user is still running, the
+     * volumes will be re-mounted as well.
+     *
+     * @param userId the userId for which to reset storage
+     */
+    public abstract void resetUser(int userId);
+
+    /**
+     * Returns {@code true} if the immediate last installed version of an app with {@code uid} had
+     * legacy storage, {@code false} otherwise.
+     */
+    public abstract boolean hasLegacyExternalStorage(int uid);
+
+    /**
+     * Makes sure app-private data directories on external storage are setup correctly
+     * after an application is installed or upgraded. The main use for this is OBB dirs,
+     * which can be created/modified by the installer.
+     *
+     * @param packageName the package name of the package
+     * @param uid the uid of the package
+     */
+    public abstract void prepareAppDataAfterInstall(@NonNull String packageName, int uid);
+
+    /**
+     * Return true if uid is external storage service.
+     */
+    public abstract boolean isExternalStorageService(int uid);
+
+    /**
+     * Frees cache held by ExternalStorageService.
+     *
+     * <p> Blocks until the service frees the cache or fails in doing so.
+     *
+     * @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed,
+     *                   null value indicates private internal volume.
+     * @param bytes number of bytes which need to be freed
+     */
+    public abstract void freeCache(@Nullable String volumeUuid, long bytes);
+
+    /**
+     * Returns the {@link VolumeInfo#getId()} values for the volumes matching
+     * {@link VolumeInfo#isPrimary()}
+     */
+    public abstract List<String> getPrimaryVolumeIds();
+
+    /**
+     * Tells StorageManager that CE storage for this user has been prepared.
+     *
+     * @param userId userId for which CE storage has been prepared
+     */
+    public abstract void markCeStoragePrepared(@UserIdInt int userId);
+
+    /**
+     * Returns true when CE storage for this user has been prepared.
+     *
+     * When the user key is unlocked and CE storage has been prepared,
+     * it's ok to access and modify CE directories on volumes for this user.
+     */
+    public abstract boolean isCeStoragePrepared(@UserIdInt int userId);
+
+    /**
+     * A listener for changes to the cloud provider.
+     */
+    public interface CloudProviderChangeListener {
+        /**
+         * Triggered when the cloud provider changes. A {@code null} value means there's currently
+         * no cloud provider.
+         */
+        void onCloudProviderChanged(int userId, @Nullable String authority);
+    }
+
+    /**
+     * Register a {@link CloudProviderChangeListener} to be notified when a cloud media provider
+     * changes. The listener will be called after registration with any currently set cloud media
+     * providers.
+     */
+    public abstract void registerCloudProviderChangeListener(
+            @NonNull CloudProviderChangeListener listener);
+}
diff --git a/android-34/android/os/storage/StorageVolume.java b/android-34/android/os/storage/StorageVolume.java
new file mode 100644
index 0000000..e1f112a
--- /dev/null
+++ b/android-34/android/os/storage/StorageVolume.java
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2011 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.os.storage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.util.Locale;
+import java.util.UUID;
+
+/**
+ * Information about a shared/external storage volume for a specific user.
+ *
+ * <p>
+ * A device always has one (and one only) primary storage volume, but it could have extra volumes,
+ * like SD cards and USB drives. This object represents the logical view of a storage
+ * volume for a specific user: different users might have different views for the same physical
+ * volume (for example, if the volume is a built-in emulated storage).
+ *
+ * <p>
+ * The storage volume is not necessarily mounted, applications should use {@link #getState()} to
+ * verify its state.
+ *
+ * <p>
+ * Applications willing to read or write to this storage volume needs to get a permission from the
+ * user first, which can be achieved in the following ways:
+ *
+ * <ul>
+ * <li>To get access to standard directories (like the {@link Environment#DIRECTORY_PICTURES}), they
+ * can use the {@link #createAccessIntent(String)}. This is the recommend way, since it provides a
+ * simpler API and narrows the access to the given directory (and its descendants).
+ * <li>To get access to any directory (and its descendants), they can use the Storage Acess
+ * Framework APIs (such as {@link Intent#ACTION_OPEN_DOCUMENT} and
+ * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, although these APIs do not guarantee the user will
+ * select this specific volume.
+ * <li>To get read and write access to the primary storage volume, applications can declare the
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions respectively, with the
+ * latter including the former. This approach is discouraged, since users may be hesitant to grant
+ * broad access to all files contained on a storage device.
+ * </ul>
+ *
+ * <p>It can be obtained through {@link StorageManager#getStorageVolumes()} and
+ * {@link StorageManager#getPrimaryStorageVolume()} and also as an extra in some broadcasts
+ * (see {@link #EXTRA_STORAGE_VOLUME}).
+ *
+ * <p>
+ * See {@link Environment#getExternalStorageDirectory()} for more info about shared/external
+ * storage semantics.
+ */
+// NOTE: This is a legacy specialization of VolumeInfo which describes the volume for a specific
+// user, but is now part of the public API.
+public final class StorageVolume implements Parcelable {
+
+    @UnsupportedAppUsage
+    private final String mId;
+    @UnsupportedAppUsage
+    private final File mPath;
+    private final File mInternalPath;
+    @UnsupportedAppUsage
+    private final String mDescription;
+    @UnsupportedAppUsage
+    private final boolean mPrimary;
+    @UnsupportedAppUsage
+    private final boolean mRemovable;
+    private final boolean mEmulated;
+    private final boolean mExternallyManaged;
+    private final boolean mAllowMassStorage;
+    private final long mMaxFileSize;
+    private final UserHandle mOwner;
+    private final UUID mUuid;
+    private final String mFsUuid;
+    private final String mState;
+
+    /**
+     * Name of the {@link Parcelable} extra in the {@link Intent#ACTION_MEDIA_REMOVED},
+     * {@link Intent#ACTION_MEDIA_UNMOUNTED}, {@link Intent#ACTION_MEDIA_CHECKING},
+     * {@link Intent#ACTION_MEDIA_NOFS}, {@link Intent#ACTION_MEDIA_MOUNTED},
+     * {@link Intent#ACTION_MEDIA_SHARED}, {@link Intent#ACTION_MEDIA_BAD_REMOVAL},
+     * {@link Intent#ACTION_MEDIA_UNMOUNTABLE}, and {@link Intent#ACTION_MEDIA_EJECT} broadcast that
+     * contains a {@link StorageVolume}.
+     */
+    // Also sent on ACTION_MEDIA_UNSHARED, which is @hide
+    public static final String EXTRA_STORAGE_VOLUME = "android.os.storage.extra.STORAGE_VOLUME";
+
+    /**
+     * Name of the String extra used by {@link #createAccessIntent(String) createAccessIntent}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_DIRECTORY_NAME = "android.os.storage.extra.DIRECTORY_NAME";
+
+    /**
+     * Name of the intent used by {@link #createAccessIntent(String) createAccessIntent}.
+     */
+    private static final String ACTION_OPEN_EXTERNAL_DIRECTORY =
+            "android.os.storage.action.OPEN_EXTERNAL_DIRECTORY";
+
+    /** {@hide} */
+    public static final int STORAGE_ID_INVALID = 0x00000000;
+    /** {@hide} */
+    public static final int STORAGE_ID_PRIMARY = 0x00010001;
+
+    /** {@hide} */
+    public StorageVolume(String id, File path, File internalPath, String description,
+            boolean primary, boolean removable, boolean emulated, boolean externallyManaged,
+            boolean allowMassStorage, long maxFileSize, UserHandle owner, UUID uuid, String fsUuid,
+            String state) {
+        mId = Preconditions.checkNotNull(id);
+        mPath = Preconditions.checkNotNull(path);
+        mInternalPath = Preconditions.checkNotNull(internalPath);
+        mDescription = Preconditions.checkNotNull(description);
+        mPrimary = primary;
+        mRemovable = removable;
+        mEmulated = emulated;
+        mExternallyManaged = externallyManaged;
+        mAllowMassStorage = allowMassStorage;
+        mMaxFileSize = maxFileSize;
+        mOwner = Preconditions.checkNotNull(owner);
+        mUuid = uuid;
+        mFsUuid = fsUuid;
+        mState = Preconditions.checkNotNull(state);
+    }
+
+    private StorageVolume(Parcel in) {
+        mId = in.readString8();
+        mPath = new File(in.readString8());
+        mInternalPath = new File(in.readString8());
+        mDescription = in.readString8();
+        mPrimary = in.readInt() != 0;
+        mRemovable = in.readInt() != 0;
+        mEmulated = in.readInt() != 0;
+        mExternallyManaged = in.readInt() != 0;
+        mAllowMassStorage = in.readInt() != 0;
+        mMaxFileSize = in.readLong();
+        mOwner = in.readParcelable(null, android.os.UserHandle.class);
+        if (in.readInt() != 0) {
+            mUuid = StorageManager.convert(in.readString8());
+        } else {
+            mUuid = null;
+        }
+        mFsUuid = in.readString8();
+        mState = in.readString8();
+    }
+
+    /**
+     * Return an opaque ID that can be used to identify this volume.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the mount path for the volume.
+     *
+     * @return the mount path
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@link StorageVolume#getDirectory()}")
+    @TestApi
+    public String getPath() {
+        return mPath.toString();
+    }
+
+    /**
+     * Returns the path of the underlying filesystem.
+     *
+     * @return the internal path
+     * @hide
+     */
+    public String getInternalPath() {
+        return mInternalPath.toString();
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@link StorageVolume#getDirectory()}")
+    public File getPathFile() {
+        return mPath;
+    }
+
+    /**
+     * Returns the directory where this volume is currently mounted.
+     * <p>
+     * Direct filesystem access via this path has significant emulation
+     * overhead, and apps are instead strongly encouraged to interact with media
+     * on storage volumes via the {@link MediaStore} APIs.
+     * <p>
+     * This directory does not give apps any additional access beyond what they
+     * already have via {@link MediaStore}.
+     *
+     * @return directory where this volume is mounted, or {@code null} if the
+     *         volume is not currently mounted.
+     */
+    public @Nullable File getDirectory() {
+        switch (mState) {
+            case Environment.MEDIA_MOUNTED:
+            case Environment.MEDIA_MOUNTED_READ_ONLY:
+                return mPath;
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Returns a user-visible description of the volume.
+     *
+     * @return the volume description
+     */
+    public String getDescription(Context context) {
+        return mDescription;
+    }
+
+    /**
+     * Returns true if the volume is the primary shared/external storage, which is the volume
+     * backed by {@link Environment#getExternalStorageDirectory()}.
+     */
+    public boolean isPrimary() {
+        return mPrimary;
+    }
+
+    /**
+     * Returns true if the volume is removable.
+     *
+     * @return is removable
+     */
+    public boolean isRemovable() {
+        return mRemovable;
+    }
+
+    /**
+     * Returns true if the volume is emulated.
+     *
+     * @return is emulated
+     */
+    public boolean isEmulated() {
+        return mEmulated;
+    }
+
+    /**
+     * Returns true if the volume is managed from outside Android.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isExternallyManaged() {
+        return mExternallyManaged;
+    }
+
+    /**
+     * Returns true if this volume can be shared via USB mass storage.
+     *
+     * @return whether mass storage is allowed
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean allowMassStorage() {
+        return mAllowMassStorage;
+    }
+
+    /**
+     * Returns maximum file size for the volume, or zero if it is unbounded.
+     *
+     * @return maximum file size
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public long getMaxFileSize() {
+        return mMaxFileSize;
+    }
+
+    /**
+     * Returns the user that owns this volume
+     */
+    // TODO(b/193460475) : Android Lint handle API change from systemApi to public Api incorrectly
+    @SuppressLint("NewApi")
+    public @NonNull UserHandle getOwner() {
+        return mOwner;
+    }
+
+    /**
+     * Gets the converted volume UUID. If a valid UUID is returned, it is compatible with other
+     * APIs that make use of {@link UUID} like {@link StorageManager#allocateBytes} and
+     * {@link android.content.pm.ApplicationInfo#storageUuid}
+     *
+     * @return the UUID for the volume or {@code null} for "portable" storage devices which haven't
+     * been adopted.
+     *
+     * @see <a href="https://source.android.com/devices/storage/adoptable">Adoptable storage</a>
+     */
+    public @Nullable UUID getStorageUuid() {
+        return mUuid;
+    }
+
+    /**
+     * Gets the volume UUID, if any.
+     */
+    public @Nullable String getUuid() {
+        return mFsUuid;
+    }
+
+    /**
+     * Return the volume name that can be used to interact with this storage
+     * device through {@link MediaStore}.
+     *
+     * @return opaque volume name, or {@code null} if this volume is not indexed
+     *         by {@link MediaStore}.
+     * @see android.provider.MediaStore.Audio.Media#getContentUri(String)
+     * @see android.provider.MediaStore.Video.Media#getContentUri(String)
+     * @see android.provider.MediaStore.Images.Media#getContentUri(String)
+     */
+    public @Nullable String getMediaStoreVolumeName() {
+        if (isPrimary()) {
+            return MediaStore.VOLUME_EXTERNAL_PRIMARY;
+        } else {
+            return getNormalizedUuid();
+        }
+    }
+
+    /** {@hide} */
+    public static @Nullable String normalizeUuid(@Nullable String fsUuid) {
+        return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+    }
+
+    /** {@hide} */
+    public @Nullable String getNormalizedUuid() {
+        return normalizeUuid(mFsUuid);
+    }
+
+    /**
+     * Parse and return volume UUID as FAT volume ID, or return -1 if unable to
+     * parse or UUID is unknown.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int getFatVolumeId() {
+        if (mFsUuid == null || mFsUuid.length() != 9) {
+            return -1;
+        }
+        try {
+            return (int) Long.parseLong(mFsUuid.replace("-", ""), 16);
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public String getUserLabel() {
+        return mDescription;
+    }
+
+    /**
+     * Returns the current state of the volume.
+     *
+     * @return one of {@link Environment#MEDIA_UNKNOWN}, {@link Environment#MEDIA_REMOVED},
+     *         {@link Environment#MEDIA_UNMOUNTED}, {@link Environment#MEDIA_CHECKING},
+     *         {@link Environment#MEDIA_NOFS}, {@link Environment#MEDIA_MOUNTED},
+     *         {@link Environment#MEDIA_MOUNTED_READ_ONLY}, {@link Environment#MEDIA_SHARED},
+     *         {@link Environment#MEDIA_BAD_REMOVAL}, or {@link Environment#MEDIA_UNMOUNTABLE}.
+     */
+    public String getState() {
+        return mState;
+    }
+
+    /**
+     * Builds an intent to give access to a standard storage directory or entire volume after
+     * obtaining the user's approval.
+     * <p>
+     * When invoked, the system will ask the user to grant access to the requested directory (and
+     * its descendants). The result of the request will be returned to the activity through the
+     * {@code onActivityResult} method.
+     * <p>
+     * To gain access to descendants (child, grandchild, etc) documents, use
+     * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)}, or
+     * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} with the returned URI.
+     * <p>
+     * If your application only needs to store internal data, consider using
+     * {@link Context#getExternalFilesDirs(String) Context.getExternalFilesDirs},
+     * {@link Context#getExternalCacheDirs()}, or {@link Context#getExternalMediaDirs()}, which
+     * require no permissions to read or write.
+     * <p>
+     * Access to the entire volume is only available for non-primary volumes (for the primary
+     * volume, apps can use the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions) and should be used
+     * with caution, since users are more likely to deny access when asked for entire volume access
+     * rather than specific directories.
+     *
+     * @param directoryName must be one of {@link Environment#DIRECTORY_MUSIC},
+     *            {@link Environment#DIRECTORY_PODCASTS}, {@link Environment#DIRECTORY_RINGTONES},
+     *            {@link Environment#DIRECTORY_ALARMS}, {@link Environment#DIRECTORY_NOTIFICATIONS},
+     *            {@link Environment#DIRECTORY_PICTURES}, {@link Environment#DIRECTORY_MOVIES},
+     *            {@link Environment#DIRECTORY_DOWNLOADS}, {@link Environment#DIRECTORY_DCIM}, or
+     *            {@link Environment#DIRECTORY_DOCUMENTS}, or {@code null} to request access to the
+     *            entire volume.
+     * @return intent to request access, or {@code null} if the requested directory is invalid for
+     *         that volume.
+     * @see DocumentsContract
+     * @deprecated Callers should migrate to using {@link Intent#ACTION_OPEN_DOCUMENT_TREE} instead.
+     *             Launching this {@link Intent} on devices running
+     *             {@link android.os.Build.VERSION_CODES#Q} or higher, will immediately finish
+     *             with a result code of {@link android.app.Activity#RESULT_CANCELED}.
+     */
+    @Deprecated
+    public @Nullable Intent createAccessIntent(String directoryName) {
+        if ((isPrimary() && directoryName == null) ||
+                (directoryName != null && !Environment.isStandardDirectory(directoryName))) {
+            return null;
+        }
+        final Intent intent = new Intent(ACTION_OPEN_EXTERNAL_DIRECTORY);
+        intent.putExtra(EXTRA_STORAGE_VOLUME, this);
+        intent.putExtra(EXTRA_DIRECTORY_NAME, directoryName);
+        return intent;
+    }
+
+    /**
+     * Builds an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to allow the user to grant access to any
+     * directory subtree (or entire volume) from the {@link android.provider.DocumentsProvider}s
+     * available on the device. The initial location of the document navigation will be the root of
+     * this {@link StorageVolume}.
+     *
+     * Note that the returned {@link Intent} simply suggests that the user picks this {@link
+     * StorageVolume} by default, but the user may select a different location. Callers must respect
+     * the user's chosen location, even if it is different from the originally requested location.
+     *
+     * @return intent to {@link Intent#ACTION_OPEN_DOCUMENT_TREE} initially showing the contents
+     *         of this {@link StorageVolume}
+     * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+     */
+    @NonNull public Intent createOpenDocumentTreeIntent() {
+        final String rootId = isEmulated()
+                ? DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID
+                : mFsUuid;
+        final Uri rootUri = DocumentsContract.buildRootUri(
+                DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId);
+        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
+                .putExtra(DocumentsContract.EXTRA_INITIAL_URI, rootUri)
+                .putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+        return intent;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj instanceof StorageVolume && mPath != null) {
+            StorageVolume volume = (StorageVolume)obj;
+            return (mPath.equals(volume.mPath));
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mPath.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buffer = new StringBuilder("StorageVolume: ").append(mDescription);
+        if (mFsUuid != null) {
+            buffer.append(" (").append(mFsUuid).append(")");
+        }
+        return buffer.toString();
+    }
+
+    /** {@hide} */
+    // TODO: find out where toString() is called internally and replace these calls by dump().
+    public String dump() {
+        final CharArrayWriter writer = new CharArrayWriter();
+        dump(new IndentingPrintWriter(writer, "    ", 80));
+        return writer.toString();
+    }
+
+    /** {@hide} */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("StorageVolume:");
+        pw.increaseIndent();
+        pw.printPair("mId", mId);
+        pw.printPair("mPath", mPath);
+        pw.printPair("mInternalPath", mInternalPath);
+        pw.printPair("mDescription", mDescription);
+        pw.printPair("mPrimary", mPrimary);
+        pw.printPair("mRemovable", mRemovable);
+        pw.printPair("mEmulated", mEmulated);
+        pw.printPair("mExternallyManaged", mExternallyManaged);
+        pw.printPair("mAllowMassStorage", mAllowMassStorage);
+        pw.printPair("mMaxFileSize", mMaxFileSize);
+        pw.printPair("mOwner", mOwner);
+        pw.printPair("mFsUuid", mFsUuid);
+        pw.printPair("mState", mState);
+        pw.decreaseIndent();
+    }
+
+    public static final @android.annotation.NonNull Creator<StorageVolume> CREATOR = new Creator<StorageVolume>() {
+        @Override
+        public StorageVolume createFromParcel(Parcel in) {
+            return new StorageVolume(in);
+        }
+
+        @Override
+        public StorageVolume[] newArray(int size) {
+            return new StorageVolume[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString8(mId);
+        parcel.writeString8(mPath.toString());
+        parcel.writeString8(mInternalPath.toString());
+        parcel.writeString8(mDescription);
+        parcel.writeInt(mPrimary ? 1 : 0);
+        parcel.writeInt(mRemovable ? 1 : 0);
+        parcel.writeInt(mEmulated ? 1 : 0);
+        parcel.writeInt(mExternallyManaged ? 1 : 0);
+        parcel.writeInt(mAllowMassStorage ? 1 : 0);
+        parcel.writeLong(mMaxFileSize);
+        parcel.writeParcelable(mOwner, flags);
+        if (mUuid != null) {
+            parcel.writeInt(1);
+            parcel.writeString8(StorageManager.convert(mUuid));
+        } else {
+            parcel.writeInt(0);
+        }
+        parcel.writeString8(mFsUuid);
+        parcel.writeString8(mState);
+    }
+
+    /** @hide */
+    // This class is used by the mainline test suite, so we have to keep these APIs around across
+    // releases. Consider making this class public to help external developers to write tests as
+    // well.
+    @TestApi
+    public static final class Builder {
+        private String mId;
+        private File mPath;
+        private String mDescription;
+        private boolean mPrimary;
+        private boolean mRemovable;
+        private boolean mEmulated;
+        private UserHandle mOwner;
+        private UUID mStorageUuid;
+        private String mUuid;
+        private String mState;
+
+        @SuppressLint("StreamFiles")
+        public Builder(
+                @NonNull String id, @NonNull File path, @NonNull String description,
+                @NonNull UserHandle owner, @NonNull String state) {
+            mId = id;
+            mPath = path;
+            mDescription = description;
+            mOwner = owner;
+            mState = state;
+        }
+
+        @NonNull
+        public Builder setStorageUuid(@Nullable UUID storageUuid) {
+            mStorageUuid = storageUuid;
+            return this;
+        }
+
+        @NonNull
+        public Builder setUuid(@Nullable String uuid) {
+            mUuid = uuid;
+            return this;
+        }
+
+        @NonNull
+        public Builder setPrimary(boolean primary) {
+            mPrimary = primary;
+            return this;
+        }
+
+        @NonNull
+        public Builder setRemovable(boolean removable) {
+            mRemovable = removable;
+            return this;
+        }
+
+        @NonNull
+        public Builder setEmulated(boolean emulated) {
+            mEmulated = emulated;
+            return this;
+        }
+
+        @NonNull
+        public StorageVolume build() {
+            return new StorageVolume(
+                    mId,
+                    mPath,
+                    /* internalPath= */ mPath,
+                    mDescription,
+                    mPrimary,
+                    mRemovable,
+                    mEmulated,
+                    /* externallyManaged= */ false,
+                    /* allowMassStorage= */ false,
+                    /* maxFileSize= */ 0,
+                    mOwner,
+                    mStorageUuid,
+                    mUuid,
+                    mState);
+        }
+    }
+
+}
diff --git a/android-34/android/os/storage/VolumeInfo.java b/android-34/android/os/storage/VolumeInfo.java
new file mode 100644
index 0000000..84b6b5e
--- /dev/null
+++ b/android-34/android/os/storage/VolumeInfo.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2015 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.os.storage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IVold;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.provider.DocumentsContract;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Information about a storage volume that may be mounted. A volume may be a
+ * partition on a physical {@link DiskInfo}, an emulated volume above some other
+ * storage medium, or a standalone container like an ASEC or OBB.
+ * <p>
+ * Volumes may be mounted with various flags:
+ * <ul>
+ * <li>{@link #MOUNT_FLAG_PRIMARY} means the volume provides primary external
+ * storage, historically found at {@code /sdcard}.
+ * <li>{@link #MOUNT_FLAG_VISIBLE_FOR_READ} and
+ * {@link #MOUNT_FLAG_VISIBLE_FOR_WRITE} mean the volume is visible to
+ * third-party apps for direct filesystem access. The system should send out
+ * relevant storage broadcasts and index any media on visible volumes. Visible
+ * volumes are considered a more stable part of the device, which is why we take
+ * the time to index them. In particular, transient volumes like USB OTG devices
+ * <em>should not</em> be marked as visible; their contents should be surfaced
+ * to apps through the Storage Access Framework.
+ * </ul>
+ *
+ * @hide
+ */
+public class VolumeInfo implements Parcelable {
+    public static final String ACTION_VOLUME_STATE_CHANGED =
+            "android.os.storage.action.VOLUME_STATE_CHANGED";
+    public static final String EXTRA_VOLUME_ID =
+            "android.os.storage.extra.VOLUME_ID";
+    public static final String EXTRA_VOLUME_STATE =
+            "android.os.storage.extra.VOLUME_STATE";
+
+    /** Stub volume representing internal private storage */
+    public static final String ID_PRIVATE_INTERNAL = "private";
+    /** Real volume representing internal emulated storage */
+    public static final String ID_EMULATED_INTERNAL = "emulated";
+
+    @UnsupportedAppUsage
+    public static final int TYPE_PUBLIC = IVold.VOLUME_TYPE_PUBLIC;
+    public static final int TYPE_PRIVATE = IVold.VOLUME_TYPE_PRIVATE;
+    @UnsupportedAppUsage
+    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 TYPE_STUB = IVold.VOLUME_TYPE_STUB;
+
+    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_FOR_READ = IVold.MOUNT_FLAG_VISIBLE_FOR_READ;
+    public static final int MOUNT_FLAG_VISIBLE_FOR_WRITE = IVold.MOUNT_FLAG_VISIBLE_FOR_WRITE;
+
+    private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
+    private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
+    private static SparseIntArray sStateToDescrip = new SparseIntArray();
+
+    private static final Comparator<VolumeInfo>
+            sDescriptionComparator = new Comparator<VolumeInfo>() {
+        @Override
+        public int compare(VolumeInfo lhs, VolumeInfo rhs) {
+            if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) {
+                return -1;
+            } else if (lhs.getDescription() == null) {
+                return 1;
+            } else if (rhs.getDescription() == null) {
+                return -1;
+            } else {
+                return lhs.getDescription().compareTo(rhs.getDescription());
+            }
+        }
+    };
+
+    static {
+        sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
+        sStateToEnvironment.put(VolumeInfo.STATE_CHECKING, Environment.MEDIA_CHECKING);
+        sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED);
+        sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, Environment.MEDIA_MOUNTED_READ_ONLY);
+        sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
+        sStateToEnvironment.put(VolumeInfo.STATE_EJECTING, Environment.MEDIA_EJECTING);
+        sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE);
+        sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED);
+        sStateToEnvironment.put(VolumeInfo.STATE_BAD_REMOVAL, Environment.MEDIA_BAD_REMOVAL);
+
+        sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL);
+
+        sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTED, R.string.ext_media_status_unmounted);
+        sStateToDescrip.put(VolumeInfo.STATE_CHECKING, R.string.ext_media_status_checking);
+        sStateToDescrip.put(VolumeInfo.STATE_MOUNTED, R.string.ext_media_status_mounted);
+        sStateToDescrip.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, R.string.ext_media_status_mounted_ro);
+        sStateToDescrip.put(VolumeInfo.STATE_FORMATTING, R.string.ext_media_status_formatting);
+        sStateToDescrip.put(VolumeInfo.STATE_EJECTING, R.string.ext_media_status_ejecting);
+        sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTABLE, R.string.ext_media_status_unmountable);
+        sStateToDescrip.put(VolumeInfo.STATE_REMOVED, R.string.ext_media_status_removed);
+        sStateToDescrip.put(VolumeInfo.STATE_BAD_REMOVAL, R.string.ext_media_status_bad_removal);
+    }
+
+    /** vold state */
+    public final String id;
+    @UnsupportedAppUsage
+    public final int type;
+    @UnsupportedAppUsage
+    public final DiskInfo disk;
+    public final String partGuid;
+    public int mountFlags = 0;
+    public int mountUserId = UserHandle.USER_NULL;
+    @UnsupportedAppUsage
+    public int state = STATE_UNMOUNTED;
+    public String fsType;
+    @UnsupportedAppUsage
+    public String fsUuid;
+    @UnsupportedAppUsage
+    public String fsLabel;
+    @UnsupportedAppUsage
+    public String path;
+    @UnsupportedAppUsage
+    public String internalPath;
+
+    public VolumeInfo(String id, int type, DiskInfo disk, String partGuid) {
+        this.id = Preconditions.checkNotNull(id);
+        this.type = type;
+        this.disk = disk;
+        this.partGuid = partGuid;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public VolumeInfo(Parcel parcel) {
+        id = parcel.readString8();
+        type = parcel.readInt();
+        if (parcel.readInt() != 0) {
+            disk = DiskInfo.CREATOR.createFromParcel(parcel);
+        } else {
+            disk = null;
+        }
+        partGuid = parcel.readString8();
+        mountFlags = parcel.readInt();
+        mountUserId = parcel.readInt();
+        state = parcel.readInt();
+        fsType = parcel.readString8();
+        fsUuid = parcel.readString8();
+        fsLabel = parcel.readString8();
+        path = parcel.readString8();
+        internalPath = parcel.readString8();
+    }
+
+    public VolumeInfo(VolumeInfo volumeInfo) {
+        this.id = volumeInfo.id;
+        this.type = volumeInfo.type;
+        this.disk = volumeInfo.disk;
+        this.partGuid = volumeInfo.partGuid;
+        this.mountFlags = volumeInfo.mountFlags;
+        this.mountUserId = volumeInfo.mountUserId;
+        this.state = volumeInfo.state;
+        this.fsType = volumeInfo.fsType;
+        this.fsUuid = volumeInfo.fsUuid;
+        this.fsLabel = volumeInfo.fsLabel;
+        this.path = volumeInfo.path;
+        this.internalPath = volumeInfo.internalPath;
+    }
+
+    @UnsupportedAppUsage
+    public static @NonNull String getEnvironmentForState(int state) {
+        final String envState = sStateToEnvironment.get(state);
+        if (envState != null) {
+            return envState;
+        } else {
+            return Environment.MEDIA_UNKNOWN;
+        }
+    }
+
+    public static @Nullable String getBroadcastForEnvironment(String envState) {
+        return sEnvironmentToBroadcast.get(envState);
+    }
+
+    public static @Nullable String getBroadcastForState(int state) {
+        return getBroadcastForEnvironment(getEnvironmentForState(state));
+    }
+
+    public static @NonNull Comparator<VolumeInfo> getDescriptionComparator() {
+        return sDescriptionComparator;
+    }
+
+    @UnsupportedAppUsage
+    public @NonNull String getId() {
+        return id;
+    }
+
+    @UnsupportedAppUsage
+    public @Nullable DiskInfo getDisk() {
+        return disk;
+    }
+
+    @UnsupportedAppUsage
+    public @Nullable String getDiskId() {
+        return (disk != null) ? disk.id : null;
+    }
+
+    @UnsupportedAppUsage
+    public int getType() {
+        return type;
+    }
+
+    @UnsupportedAppUsage
+    public int getState() {
+        return state;
+    }
+
+    public int getStateDescription() {
+        return sStateToDescrip.get(state, 0);
+    }
+
+    @UnsupportedAppUsage
+    public @Nullable String getFsUuid() {
+        return fsUuid;
+    }
+
+    public @Nullable String getNormalizedFsUuid() {
+        return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+    }
+
+    @UnsupportedAppUsage
+    public int getMountUserId() {
+        return mountUserId;
+    }
+
+    @UnsupportedAppUsage
+    public @Nullable String getDescription() {
+        if (ID_PRIVATE_INTERNAL.equals(id) || id.startsWith(ID_EMULATED_INTERNAL + ";")) {
+            return Resources.getSystem().getString(com.android.internal.R.string.storage_internal);
+        } else if (!TextUtils.isEmpty(fsLabel)) {
+            return fsLabel;
+        } else {
+            return null;
+        }
+    }
+
+    @UnsupportedAppUsage
+    public boolean isMountedReadable() {
+        return state == STATE_MOUNTED || state == STATE_MOUNTED_READ_ONLY;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isMountedWritable() {
+        return state == STATE_MOUNTED;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isPrimary() {
+        return (mountFlags & MOUNT_FLAG_PRIMARY) != 0;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isPrimaryPhysical() {
+        return isPrimary() && (getType() == TYPE_PUBLIC);
+    }
+
+    private boolean isVisibleForRead() {
+        return (mountFlags & MOUNT_FLAG_VISIBLE_FOR_READ) != 0;
+    }
+
+    private boolean isVisibleForWrite() {
+        return (mountFlags & MOUNT_FLAG_VISIBLE_FOR_WRITE) != 0;
+    }
+
+    @UnsupportedAppUsage
+    public boolean isVisible() {
+        return isVisibleForRead() || isVisibleForWrite();
+    }
+
+    private boolean isVolumeSupportedForUser(int userId) {
+        if (mountUserId != userId) {
+            return false;
+        }
+        return type == TYPE_PUBLIC || type == TYPE_STUB || type == TYPE_EMULATED;
+    }
+
+    /**
+     * Returns {@code true} if this volume is visible for {@code userId}, {@code false} otherwise.
+     */
+    public boolean isVisibleForUser(int userId) {
+        return isVolumeSupportedForUser(userId) && isVisible();
+    }
+
+    /**
+     * Returns {@code true} if this volume is the primary emulated volume for {@code userId},
+     * {@code false} otherwise.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean isPrimaryEmulatedForUser(int userId) {
+        return id.equals(ID_EMULATED_INTERNAL + ";" + userId);
+    }
+
+    public boolean isVisibleForRead(int userId) {
+        return isVolumeSupportedForUser(userId) && isVisibleForRead();
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean isVisibleForWrite(int userId) {
+        return isVolumeSupportedForUser(userId) && isVisibleForWrite();
+    }
+
+    @UnsupportedAppUsage
+    public File getPath() {
+        return (path != null) ? new File(path) : null;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public File getInternalPath() {
+        return (internalPath != null) ? new File(internalPath) : null;
+    }
+
+    @UnsupportedAppUsage
+    public File getPathForUser(int userId) {
+        if (path == null) {
+            return null;
+        } else if (type == TYPE_PUBLIC || type == TYPE_STUB) {
+            return new File(path);
+        } else if (type == TYPE_EMULATED) {
+            return new File(path, Integer.toString(userId));
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Path which is accessible to apps holding
+     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
+     */
+    @UnsupportedAppUsage
+    public File getInternalPathForUser(int userId) {
+        if (path == null) {
+            return null;
+        } else if (type == TYPE_PUBLIC || type == TYPE_STUB) {
+            // TODO: plumb through cleaner path from vold
+            return new File(path.replace("/storage/", "/mnt/media_rw/"));
+        } else {
+            return getPathForUser(userId);
+        }
+    }
+
+    @UnsupportedAppUsage
+    public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) {
+        final StorageManager storage = context.getSystemService(StorageManager.class);
+
+        final boolean removable;
+        final boolean emulated;
+        final boolean externallyManaged = type == TYPE_STUB;
+        final boolean allowMassStorage = false;
+        final String envState = reportUnmounted
+                ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
+
+        File userPath = getPathForUser(userId);
+        if (userPath == null) {
+            userPath = new File("/dev/null");
+        }
+        File internalPath = getInternalPathForUser(userId);
+        if (internalPath == null) {
+            internalPath = new File("/dev/null");
+        }
+
+        String description = null;
+        UUID uuid = null;
+        String derivedFsUuid = fsUuid;
+        long maxFileSize = 0;
+
+        if (type == TYPE_EMULATED) {
+            emulated = true;
+
+            final VolumeInfo privateVol = storage.findPrivateForEmulated(this);
+            if (privateVol != null) {
+                description = storage.getBestVolumeDescription(privateVol);
+                uuid = StorageManager.convert(privateVol.fsUuid);
+                derivedFsUuid = privateVol.fsUuid;
+            } else {
+                uuid = StorageManager.UUID_DEFAULT;
+            }
+
+            if (isPrimaryEmulatedForUser(userId)) {
+                removable = false;
+            } else {
+                removable = true;
+            }
+
+        } else if (type == TYPE_PUBLIC || type == TYPE_STUB) {
+            emulated = false;
+            removable = true;
+
+            description = storage.getBestVolumeDescription(this);
+
+            if ("vfat".equals(fsType)) {
+                maxFileSize = 4294967295L;
+            }
+
+        } else {
+            throw new IllegalStateException("Unexpected volume type " + type);
+        }
+
+        if (description == null) {
+            description = context.getString(android.R.string.unknownName);
+        }
+
+        return new StorageVolume(id, userPath, internalPath, description, isPrimary(), removable,
+                emulated, externallyManaged, allowMassStorage, maxFileSize, new UserHandle(userId),
+                uuid, derivedFsUuid, envState);
+    }
+
+    @UnsupportedAppUsage
+    public static int buildStableMtpStorageId(String fsUuid) {
+        if (TextUtils.isEmpty(fsUuid)) {
+            return StorageVolume.STORAGE_ID_INVALID;
+        } else {
+            int hash = 0;
+            for (int i = 0; i < fsUuid.length(); ++i) {
+                hash = 31 * hash + fsUuid.charAt(i);
+            }
+            hash = (hash ^ (hash << 16)) & 0xffff0000;
+            // Work around values that the spec doesn't allow, or that we've
+            // reserved for primary
+            if (hash == 0x00000000) hash = 0x00020000;
+            if (hash == 0x00010000) hash = 0x00020000;
+            if (hash == 0xffff0000) hash = 0xfffe0000;
+            return hash | 0x0001;
+        }
+    }
+
+    // TODO: avoid this layering violation
+    private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents";
+    private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary";
+
+    /**
+     * Build an intent to browse the contents of this volume. Only valid for
+     * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}.
+     */
+    @UnsupportedAppUsage
+    public @Nullable Intent buildBrowseIntent() {
+        return buildBrowseIntentForUser(UserHandle.myUserId());
+    }
+
+    public @Nullable Intent buildBrowseIntentForUser(int userId) {
+        final Uri uri;
+        if ((type == VolumeInfo.TYPE_PUBLIC || type == VolumeInfo.TYPE_STUB)
+                && mountUserId == userId) {
+            uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
+        } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
+            uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
+                    DOCUMENT_ROOT_PRIMARY_EMULATED);
+        } else {
+            return null;
+        }
+
+        final Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
+
+        // note that docsui treats this as *force* show advanced. So sending
+        // false permits advanced to be shown based on user preferences.
+        intent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, isPrimary());
+        return intent;
+    }
+
+    @Override
+    public String toString() {
+        final CharArrayWriter writer = new CharArrayWriter();
+        dump(new IndentingPrintWriter(writer, "    ", 80));
+        return writer.toString();
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("VolumeInfo{" + id + "}:");
+        pw.increaseIndent();
+        pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type));
+        pw.printPair("diskId", getDiskId());
+        pw.printPair("partGuid", partGuid);
+        pw.printPair("mountFlags", DebugUtils.flagsToString(getClass(), "MOUNT_FLAG_", mountFlags));
+        pw.printPair("mountUserId", mountUserId);
+        pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state));
+        pw.println();
+        pw.printPair("fsType", fsType);
+        pw.printPair("fsUuid", fsUuid);
+        pw.printPair("fsLabel", fsLabel);
+        pw.println();
+        pw.printPair("path", path);
+        pw.printPair("internalPath", internalPath);
+        pw.decreaseIndent();
+        pw.println();
+    }
+
+    @Override
+    public VolumeInfo clone() {
+        final Parcel temp = Parcel.obtain();
+        try {
+            writeToParcel(temp, 0);
+            temp.setDataPosition(0);
+            return CREATOR.createFromParcel(temp);
+        } finally {
+            temp.recycle();
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof VolumeInfo) {
+            return Objects.equals(id, ((VolumeInfo) o).id);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final @android.annotation.NonNull Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() {
+        @Override
+        public VolumeInfo createFromParcel(Parcel in) {
+            return new VolumeInfo(in);
+        }
+
+        @Override
+        public VolumeInfo[] newArray(int size) {
+            return new VolumeInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString8(id);
+        parcel.writeInt(type);
+        if (disk != null) {
+            parcel.writeInt(1);
+            disk.writeToParcel(parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
+        parcel.writeString8(partGuid);
+        parcel.writeInt(mountFlags);
+        parcel.writeInt(mountUserId);
+        parcel.writeInt(state);
+        parcel.writeString8(fsType);
+        parcel.writeString8(fsUuid);
+        parcel.writeString8(fsLabel);
+        parcel.writeString8(path);
+        parcel.writeString8(internalPath);
+    }
+}
diff --git a/android-34/android/os/storage/VolumeRecord.java b/android-34/android/os/storage/VolumeRecord.java
new file mode 100644
index 0000000..1e5cdd7
--- /dev/null
+++ b/android-34/android/os/storage/VolumeRecord.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2015 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.os.storage;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.util.DebugUtils;
+import android.util.TimeUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Metadata for a storage volume which may not be currently present.
+ *
+ * @hide
+ */
+public class VolumeRecord implements Parcelable {
+    public static final String EXTRA_FS_UUID =
+            "android.os.storage.extra.FS_UUID";
+
+    public static final int USER_FLAG_INITED = 1 << 0;
+    public static final int USER_FLAG_SNOOZED = 1 << 1;
+
+    public final int type;
+    public final String fsUuid;
+    public String partGuid;
+    public String nickname;
+    public int userFlags;
+    public long createdMillis;
+    public long lastSeenMillis;
+    public long lastTrimMillis;
+    public long lastBenchMillis;
+
+    public VolumeRecord(int type, String fsUuid) {
+        this.type = type;
+        this.fsUuid = Preconditions.checkNotNull(fsUuid);
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public VolumeRecord(Parcel parcel) {
+        type = parcel.readInt();
+        fsUuid = parcel.readString();
+        partGuid = parcel.readString();
+        nickname = parcel.readString();
+        userFlags = parcel.readInt();
+        createdMillis = parcel.readLong();
+        lastSeenMillis = parcel.readLong();
+        lastTrimMillis = parcel.readLong();
+        lastBenchMillis = parcel.readLong();
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public String getFsUuid() {
+        return fsUuid;
+    }
+
+    public String getNormalizedFsUuid() {
+        return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+    }
+
+    public String getNickname() {
+        return nickname;
+    }
+
+    public boolean isInited() {
+        return (userFlags & USER_FLAG_INITED) != 0;
+    }
+
+    public boolean isSnoozed() {
+        return (userFlags & USER_FLAG_SNOOZED) != 0;
+    }
+
+    public StorageVolume buildStorageVolume(Context context) {
+        final String id = "unknown:" + fsUuid;
+        final File userPath = new File("/dev/null");
+        final File internalPath = new File("/dev/null");
+        final boolean primary = false;
+        final boolean removable = true;
+        final boolean emulated = false;
+        final boolean externallyManaged = false;
+        final boolean allowMassStorage = false;
+        final long maxFileSize = 0;
+        final UserHandle user = new UserHandle(UserHandle.USER_NULL);
+        final String envState = Environment.MEDIA_UNKNOWN;
+
+        String description = nickname;
+        if (description == null) {
+            description = context.getString(android.R.string.unknownName);
+        }
+
+        return new StorageVolume(id, userPath, internalPath, description, primary, removable,
+                emulated, externallyManaged, allowMassStorage, maxFileSize, user, null /* uuid */,
+                fsUuid, envState);
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("VolumeRecord:");
+        pw.increaseIndent();
+        pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type));
+        pw.printPair("fsUuid", fsUuid);
+        pw.printPair("partGuid", partGuid);
+        pw.println();
+        pw.printPair("nickname", nickname);
+        pw.printPair("userFlags",
+                DebugUtils.flagsToString(VolumeRecord.class, "USER_FLAG_", userFlags));
+        pw.println();
+        pw.printPair("createdMillis", TimeUtils.formatForLogging(createdMillis));
+        pw.printPair("lastSeenMillis", TimeUtils.formatForLogging(lastSeenMillis));
+        pw.printPair("lastTrimMillis", TimeUtils.formatForLogging(lastTrimMillis));
+        pw.printPair("lastBenchMillis", TimeUtils.formatForLogging(lastBenchMillis));
+        pw.decreaseIndent();
+        pw.println();
+    }
+
+    @Override
+    public VolumeRecord clone() {
+        final Parcel temp = Parcel.obtain();
+        try {
+            writeToParcel(temp, 0);
+            temp.setDataPosition(0);
+            return CREATOR.createFromParcel(temp);
+        } finally {
+            temp.recycle();
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof VolumeRecord) {
+            return Objects.equals(fsUuid, ((VolumeRecord) o).fsUuid);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return fsUuid.hashCode();
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final @android.annotation.NonNull Creator<VolumeRecord> CREATOR = new Creator<VolumeRecord>() {
+        @Override
+        public VolumeRecord createFromParcel(Parcel in) {
+            return new VolumeRecord(in);
+        }
+
+        @Override
+        public VolumeRecord[] newArray(int size) {
+            return new VolumeRecord[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(type);
+        parcel.writeString(fsUuid);
+        parcel.writeString(partGuid);
+        parcel.writeString(nickname);
+        parcel.writeInt(userFlags);
+        parcel.writeLong(createdMillis);
+        parcel.writeLong(lastSeenMillis);
+        parcel.writeLong(lastTrimMillis);
+        parcel.writeLong(lastBenchMillis);
+    }
+}
diff --git a/android-34/android/os/strictmode/CleartextNetworkViolation.java b/android-34/android/os/strictmode/CleartextNetworkViolation.java
new file mode 100644
index 0000000..6a0d381
--- /dev/null
+++ b/android-34/android/os/strictmode/CleartextNetworkViolation.java
@@ -0,0 +1,23 @@
+/*
+ * 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.os.strictmode;
+
+public final class CleartextNetworkViolation extends Violation {
+    /** @hide */
+    public CleartextNetworkViolation(String msg) {
+        super(msg);
+    }
+}
diff --git a/android-34/android/os/strictmode/ContentUriWithoutPermissionViolation.java b/android-34/android/os/strictmode/ContentUriWithoutPermissionViolation.java
new file mode 100644
index 0000000..e78dc79
--- /dev/null
+++ b/android-34/android/os/strictmode/ContentUriWithoutPermissionViolation.java
@@ -0,0 +1,30 @@
+/*
+ * 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.os.strictmode;
+
+import android.net.Uri;
+
+public final class ContentUriWithoutPermissionViolation extends Violation {
+    /** @hide */
+    public ContentUriWithoutPermissionViolation(Uri uri, String location) {
+        super(
+                uri
+                        + " exposed beyond app through "
+                        + location
+                        + " without permission grant flags; did you forget"
+                        + " FLAG_GRANT_READ_URI_PERMISSION?");
+    }
+}
diff --git a/android-34/android/os/strictmode/CredentialProtectedWhileLockedViolation.java b/android-34/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
new file mode 100644
index 0000000..89cd430
--- /dev/null
+++ b/android-34/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.strictmode;
+
+import android.content.Context;
+
+/**
+ * Subclass of {@code Violation} that is used when a process accesses filesystem
+ * paths stored in credential protected storage areas while the user is locked.
+ * <p>
+ * When a user is locked, credential protected storage is unavailable, and files
+ * stored in these locations appear to not exist, which can result in subtle app
+ * bugs if they assume default behaviors or empty states. Instead, apps should
+ * store data needed while a user is locked under device protected storage
+ * areas.
+ *
+ * @see Context#createDeviceProtectedStorageContext()
+ */
+public final class CredentialProtectedWhileLockedViolation extends Violation {
+    /** @hide */
+    public CredentialProtectedWhileLockedViolation(String message) {
+        super(message);
+    }
+}
diff --git a/android-34/android/os/strictmode/CustomViolation.java b/android-34/android/os/strictmode/CustomViolation.java
new file mode 100644
index 0000000..d4ad067
--- /dev/null
+++ b/android-34/android/os/strictmode/CustomViolation.java
@@ -0,0 +1,23 @@
+/*
+ * 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.os.strictmode;
+
+public final class CustomViolation extends Violation {
+    /** @hide */
+    public CustomViolation(String name) {
+        super(name);
+    }
+}
diff --git a/android-34/android/os/strictmode/DiskReadViolation.java b/android-34/android/os/strictmode/DiskReadViolation.java
new file mode 100644
index 0000000..fad32db
--- /dev/null
+++ b/android-34/android/os/strictmode/DiskReadViolation.java
@@ -0,0 +1,23 @@
+/*
+ * 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.os.strictmode;
+
+public final class DiskReadViolation extends Violation {
+    /** @hide */
+    public DiskReadViolation() {
+        super(null);
+    }
+}
diff --git a/android-34/android/os/strictmode/DiskWriteViolation.java b/android-34/android/os/strictmode/DiskWriteViolation.java
new file mode 100644
index 0000000..cb9ca38
--- /dev/null
+++ b/android-34/android/os/strictmode/DiskWriteViolation.java
@@ -0,0 +1,23 @@
+/*
+ * 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.os.strictmode;
+
+public final class DiskWriteViolation extends Violation {
+    /** @hide */
+    public DiskWriteViolation() {
+        super(null);
+    }
+}
diff --git a/android-34/android/os/strictmode/ExplicitGcViolation.java b/android-34/android/os/strictmode/ExplicitGcViolation.java
new file mode 100644
index 0000000..c4ae82d
--- /dev/null
+++ b/android-34/android/os/strictmode/ExplicitGcViolation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.strictmode;
+
+import android.annotation.TestApi;
+
+/**
+ * See #{@link android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc()}.
+ */
+public final class ExplicitGcViolation extends Violation {
+    /** @hide */
+    public ExplicitGcViolation() {
+        super(null);
+    }
+}
diff --git a/android-34/android/os/strictmode/FileUriExposedViolation.java b/android-34/android/os/strictmode/FileUriExposedViolation.java
new file mode 100644
index 0000000..e3e6f83
--- /dev/null
+++ b/android-34/android/os/strictmode/FileUriExposedViolation.java
@@ -0,0 +1,23 @@
+/*
+ * 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.os.strictmode;
+
+public final class FileUriExposedViolation extends Violation {
+    /** @hide */
+    public FileUriExposedViolation(String msg) {
+        super(msg);
+    }
+}
diff --git a/android-34/android/os/strictmode/ImplicitDirectBootViolation.java b/android-34/android/os/strictmode/ImplicitDirectBootViolation.java
new file mode 100644
index 0000000..e52e212
--- /dev/null
+++ b/android-34/android/os/strictmode/ImplicitDirectBootViolation.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.strictmode;
+
+import android.content.pm.PackageManager;
+
+/**
+ * Subclass of {@code Violation} that is used when a process implicitly relies
+ * on automatic Direct Boot filtering.
+ *
+ * @see PackageManager#MATCH_DIRECT_BOOT_AUTO
+ */
+public final class ImplicitDirectBootViolation extends Violation {
+    /** @hide */
+    public ImplicitDirectBootViolation() {
+        super("Implicitly relying on automatic Direct Boot filtering; request explicit"
+                + " filtering with PackageManager.MATCH_DIRECT_BOOT flags");
+    }
+}
diff --git a/android-34/android/os/strictmode/IncorrectContextUseViolation.java b/android-34/android/os/strictmode/IncorrectContextUseViolation.java
new file mode 100644
index 0000000..d8c22fd
--- /dev/null
+++ b/android-34/android/os/strictmode/IncorrectContextUseViolation.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.os.strictmode;
+
+import android.annotation.NonNull;
+import android.content.Context;
+
+/**
+ * Incorrect usage of {@link Context}, such as obtaining a UI service from non-UI {@link Context}
+ * instance.
+ *
+ * @see Context#getSystemService(String)
+ * @see Context#isUiContext
+ * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
+ */
+public final class IncorrectContextUseViolation extends Violation {
+
+    public IncorrectContextUseViolation(@NonNull String message, @NonNull Throwable originStack) {
+        super(message);
+        initCause(originStack);
+    }
+}
diff --git a/android-34/android/os/strictmode/InstanceCountViolation.java b/android-34/android/os/strictmode/InstanceCountViolation.java
new file mode 100644
index 0000000..9ee2c8e
--- /dev/null
+++ b/android-34/android/os/strictmode/InstanceCountViolation.java
@@ -0,0 +1,36 @@
+/*
+ * 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.os.strictmode;
+
+public class InstanceCountViolation extends Violation {
+    private final long mInstances;
+
+    private static final StackTraceElement[] FAKE_STACK = {
+        new StackTraceElement(
+                "android.os.StrictMode", "setClassInstanceLimit", "StrictMode.java", 1)
+    };
+
+    /** @hide */
+    public InstanceCountViolation(Class klass, long instances, int limit) {
+        super(klass.toString() + "; instances=" + instances + "; limit=" + limit);
+        setStackTrace(FAKE_STACK);
+        mInstances = instances;
+    }
+
+    public long getNumberOfInstances() {
+        return mInstances;
+    }
+}
diff --git a/android-34/android/os/strictmode/IntentReceiverLeakedViolation.java b/android-34/android/os/strictmode/IntentReceiverLeakedViolation.java
new file mode 100644
index 0000000..f416c94
--- /dev/null
+++ b/android-34/android/os/strictmode/IntentReceiverLeakedViolation.java
@@ -0,0 +1,24 @@
+/*
+ * 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.os.strictmode;
+
+public final class IntentReceiverLeakedViolation extends Violation {
+    /** @hide */
+    public IntentReceiverLeakedViolation(Throwable originStack) {
+        super(null);
+        setStackTrace(originStack.getStackTrace());
+    }
+}
diff --git a/android-34/android/os/strictmode/LeakedClosableViolation.java b/android-34/android/os/strictmode/LeakedClosableViolation.java
new file mode 100644
index 0000000..a2b0283
--- /dev/null
+++ b/android-34/android/os/strictmode/LeakedClosableViolation.java
@@ -0,0 +1,29 @@
+/*
+ * 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.os.strictmode;
+
+public final class LeakedClosableViolation extends Violation {
+    /** @hide */
+    public LeakedClosableViolation(String message, Throwable allocationSite) {
+        super(message);
+        initCause(allocationSite);
+    }
+
+    /** @hide */
+    public LeakedClosableViolation(String message) {
+        super(message);
+    }
+}
diff --git a/android-34/android/os/strictmode/NetworkViolation.java b/android-34/android/os/strictmode/NetworkViolation.java
new file mode 100644
index 0000000..abcf009
--- /dev/null
+++ b/android-34/android/os/strictmode/NetworkViolation.java
@@ -0,0 +1,23 @@
+/*
+ * 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.os.strictmode;
+
+public final class NetworkViolation extends Violation {
+    /** @hide */
+    public NetworkViolation() {
+        super(null);
+    }
+}
diff --git a/android-34/android/os/strictmode/NonSdkApiUsedViolation.java b/android-34/android/os/strictmode/NonSdkApiUsedViolation.java
new file mode 100644
index 0000000..2f0cb50
--- /dev/null
+++ b/android-34/android/os/strictmode/NonSdkApiUsedViolation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.strictmode;
+
+/**
+ * Subclass of {@code Violation} that is used when a process accesses
+ * a non SDK API.
+ */
+public final class NonSdkApiUsedViolation extends Violation {
+    /** @hide */
+    public NonSdkApiUsedViolation(String message) {
+        super(message);
+    }
+}
diff --git a/android-34/android/os/strictmode/ResourceMismatchViolation.java b/android-34/android/os/strictmode/ResourceMismatchViolation.java
new file mode 100644
index 0000000..97c4499
--- /dev/null
+++ b/android-34/android/os/strictmode/ResourceMismatchViolation.java
@@ -0,0 +1,23 @@
+/*
+ * 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.os.strictmode;
+
+public final class ResourceMismatchViolation extends Violation {
+    /** @hide */
+    public ResourceMismatchViolation(Object tag) {
+        super(tag.toString());
+    }
+}
diff --git a/android-34/android/os/strictmode/ServiceConnectionLeakedViolation.java b/android-34/android/os/strictmode/ServiceConnectionLeakedViolation.java
new file mode 100644
index 0000000..2d6b58f
--- /dev/null
+++ b/android-34/android/os/strictmode/ServiceConnectionLeakedViolation.java
@@ -0,0 +1,24 @@
+/*
+ * 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.os.strictmode;
+
+public final class ServiceConnectionLeakedViolation extends Violation {
+    /** @hide */
+    public ServiceConnectionLeakedViolation(Throwable originStack) {
+        super(null);
+        setStackTrace(originStack.getStackTrace());
+    }
+}
diff --git a/android-34/android/os/strictmode/SqliteObjectLeakedViolation.java b/android-34/android/os/strictmode/SqliteObjectLeakedViolation.java
new file mode 100644
index 0000000..0200220
--- /dev/null
+++ b/android-34/android/os/strictmode/SqliteObjectLeakedViolation.java
@@ -0,0 +1,25 @@
+/*
+ * 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.os.strictmode;
+
+public final class SqliteObjectLeakedViolation extends Violation {
+
+    /** @hide */
+    public SqliteObjectLeakedViolation(String message, Throwable originStack) {
+        super(message);
+        initCause(originStack);
+    }
+}
diff --git a/android-34/android/os/strictmode/UnbufferedIoViolation.java b/android-34/android/os/strictmode/UnbufferedIoViolation.java
new file mode 100644
index 0000000..a5c326d
--- /dev/null
+++ b/android-34/android/os/strictmode/UnbufferedIoViolation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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.os.strictmode;
+
+import android.os.StrictMode.ThreadPolicy.Builder;
+
+/**
+ * See #{@link Builder#detectUnbufferedIo()}
+ */
+public final class UnbufferedIoViolation extends Violation {
+    /** @hide */
+    public UnbufferedIoViolation() {
+        super(null);
+    }
+}
diff --git a/android-34/android/os/strictmode/UnsafeIntentLaunchViolation.java b/android-34/android/os/strictmode/UnsafeIntentLaunchViolation.java
new file mode 100644
index 0000000..4abdc1b
--- /dev/null
+++ b/android-34/android/os/strictmode/UnsafeIntentLaunchViolation.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 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.os.strictmode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+
+import java.util.Objects;
+
+/**
+ * Violation raised when your app launches an {@link Intent} which originated
+ * from outside your app.
+ * <p>
+ * Violations may indicate security vulnerabilities in the design of your app,
+ * where a malicious app could trick you into granting {@link Uri} permissions
+ * or launching unexported components. Here are some typical design patterns
+ * that can be used to safely resolve these violations:
+ * <ul>
+ * <li>The ideal approach is to migrate to using a {@link PendingIntent}, which
+ * ensures that your launch is performed using the identity of the original
+ * creator, completely avoiding the security issues described above.
+ * <li>If using a {@link PendingIntent} isn't feasible, an alternative approach
+ * is to create a brand new {@link Intent} and carefully copy only specific
+ * values from the original {@link Intent} after careful validation.
+ * </ul>
+ * <p>
+ * Note that this <em>may</em> detect false-positives if your app sends itself
+ * an {@link Intent} which is first routed through the OS, such as using
+ * {@link Intent#createChooser}. In these cases, careful inspection is required
+ * to determine if the return point into your app is appropriately protected
+ * with a signature permission or marked as unexported. If the return point is
+ * not protected, your app is likely vulnerable to malicious apps.
+ */
+public final class UnsafeIntentLaunchViolation extends Violation {
+    private transient Intent mIntent;
+
+    public UnsafeIntentLaunchViolation(@NonNull Intent intent) {
+        super("Launch of unsafe intent: " + intent);
+        mIntent = Objects.requireNonNull(intent);
+    }
+
+    /** @hide */
+    public UnsafeIntentLaunchViolation(@NonNull Intent intent, @NonNull String message) {
+        super(message);
+        mIntent = Objects.requireNonNull(intent);
+    }
+
+    /**
+     * Return the {@link Intent} which caused this violation to be raised. Note
+     * that this value is not available if this violation has been serialized
+     * since intents cannot be serialized.
+     */
+    @SuppressWarnings("IntentBuilderName")
+    public @Nullable Intent getIntent() {
+        return mIntent;
+    }
+}
diff --git a/android-34/android/os/strictmode/UntaggedSocketViolation.java b/android-34/android/os/strictmode/UntaggedSocketViolation.java
new file mode 100644
index 0000000..c34d6e8
--- /dev/null
+++ b/android-34/android/os/strictmode/UntaggedSocketViolation.java
@@ -0,0 +1,24 @@
+/*
+ * 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.os.strictmode;
+
+public final class UntaggedSocketViolation extends Violation {
+    /** @hide */
+    public UntaggedSocketViolation() {
+        super("Untagged socket detected; use TrafficStats.setTrafficStatsTag() to "
+                + "track all network usage");
+    }
+}
diff --git a/android-34/android/os/strictmode/Violation.java b/android-34/android/os/strictmode/Violation.java
new file mode 100644
index 0000000..0edb78a
--- /dev/null
+++ b/android-34/android/os/strictmode/Violation.java
@@ -0,0 +1,75 @@
+/*
+ * 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.os.strictmode;
+
+/** Root class for all StrictMode violations. */
+public abstract class Violation extends Throwable {
+    private int mHashCode;
+    private boolean mHashCodeValid;
+
+    Violation(String message) {
+        super(message);
+    }
+
+    @Override
+    public int hashCode() {
+        synchronized (this) {
+            if (mHashCodeValid) {
+                return mHashCode;
+            }
+            final String message = getMessage();
+            final Throwable cause = getCause();
+            int hashCode = message != null ? message.hashCode() : getClass().hashCode();
+            hashCode = hashCode * 37 + calcStackTraceHashCode(getStackTrace());
+            hashCode = hashCode * 37 + (cause != null ? cause.toString().hashCode() : 0);
+            mHashCodeValid = true;
+            return mHashCode = hashCode;
+        }
+    }
+
+    @Override
+    public synchronized Throwable initCause(Throwable cause) {
+        mHashCodeValid = false;
+        return super.initCause(cause);
+    }
+
+    @Override
+    public void setStackTrace(StackTraceElement[] stackTrace) {
+        super.setStackTrace(stackTrace);
+        synchronized (this) {
+            mHashCodeValid = false;
+        }
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        mHashCodeValid = false;
+        return super.fillInStackTrace();
+    }
+
+    private static int calcStackTraceHashCode(final StackTraceElement[] stackTrace) {
+        int hashCode = 17;
+        if (stackTrace != null) {
+            for (int i = 0; i < stackTrace.length; i++) {
+                if (stackTrace[i] != null) {
+                    hashCode = hashCode * 37 + stackTrace[i].hashCode();
+                }
+            }
+        }
+        return hashCode;
+    }
+}
diff --git a/android-34/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java b/android-34/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
new file mode 100644
index 0000000..c328d14
--- /dev/null
+++ b/android-34/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
@@ -0,0 +1,24 @@
+/*
+ * 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.os.strictmode;
+
+public final class WebViewMethodCalledOnWrongThreadViolation extends Violation {
+    /** @hide */
+    public WebViewMethodCalledOnWrongThreadViolation(Throwable originStack) {
+        super(null);
+        setStackTrace(originStack.getStackTrace());
+    }
+}
diff --git a/android-34/android/os/vibrator/PrebakedSegment.java b/android-34/android/os/vibrator/PrebakedSegment.java
new file mode 100644
index 0000000..cc76ffa
--- /dev/null
+++ b/android-34/android/os/vibrator/PrebakedSegment.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2021 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.os.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+
+import java.util.Objects;
+
+/**
+ * Representation of {@link VibrationEffectSegment} that plays a prebaked vibration effect.
+ *
+ * @hide
+ */
+@TestApi
+public final class PrebakedSegment extends VibrationEffectSegment {
+    private final int mEffectId;
+    private final boolean mFallback;
+    private final int mEffectStrength;
+
+    PrebakedSegment(@NonNull Parcel in) {
+        mEffectId = in.readInt();
+        mFallback = in.readByte() != 0;
+        mEffectStrength = in.readInt();
+    }
+
+    /** @hide */
+    public PrebakedSegment(int effectId, boolean shouldFallback, int effectStrength) {
+        mEffectId = effectId;
+        mFallback = shouldFallback;
+        mEffectStrength = effectStrength;
+    }
+
+    public int getEffectId() {
+        return mEffectId;
+    }
+
+    public int getEffectStrength() {
+        return mEffectStrength;
+    }
+
+    /** Return true if a fallback effect should be played if this effect is not supported. */
+    public boolean shouldFallback() {
+        return mFallback;
+    }
+
+    @Override
+    public long getDuration() {
+        return -1;
+    }
+
+    /** @hide */
+    @Override
+    public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+        if (vibrator.areAllEffectsSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
+            return true;
+        }
+        if (!mFallback) {
+            // If the Vibrator's support is not `VIBRATION_EFFECT_SUPPORT_YES`, and this effect does
+            // not support fallbacks, the effect is considered not supported by the vibrator.
+            return false;
+        }
+        // The vibrator does not have hardware support for the effect, but the effect has fallback
+        // support. Check if a fallback will be available for the effect ID.
+        switch (mEffectId) {
+            case VibrationEffect.EFFECT_CLICK:
+            case VibrationEffect.EFFECT_DOUBLE_CLICK:
+            case VibrationEffect.EFFECT_HEAVY_CLICK:
+            case VibrationEffect.EFFECT_TICK:
+                // Any of these effects are always supported via some form of fallback.
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /** @hide */
+    @Override
+    public boolean isHapticFeedbackCandidate() {
+        switch (mEffectId) {
+            case VibrationEffect.EFFECT_CLICK:
+            case VibrationEffect.EFFECT_DOUBLE_CLICK:
+            case VibrationEffect.EFFECT_HEAVY_CLICK:
+            case VibrationEffect.EFFECT_POP:
+            case VibrationEffect.EFFECT_TEXTURE_TICK:
+            case VibrationEffect.EFFECT_THUD:
+            case VibrationEffect.EFFECT_TICK:
+                return true;
+            default:
+                // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
+                return false;
+        }
+    }
+
+    /** @hide */
+    @Override
+    public boolean hasNonZeroAmplitude() {
+        return true;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PrebakedSegment resolve(int defaultAmplitude) {
+        return this;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PrebakedSegment scale(float scaleFactor) {
+        // Prebaked effect strength cannot be scaled with this method.
+        return this;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PrebakedSegment applyEffectStrength(int effectStrength) {
+        if (effectStrength != mEffectStrength && isValidEffectStrength(effectStrength)) {
+            return new PrebakedSegment(mEffectId, mFallback, effectStrength);
+        }
+        return this;
+    }
+
+    private static boolean isValidEffectStrength(int strength) {
+        switch (strength) {
+            case VibrationEffect.EFFECT_STRENGTH_LIGHT:
+            case VibrationEffect.EFFECT_STRENGTH_MEDIUM:
+            case VibrationEffect.EFFECT_STRENGTH_STRONG:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void validate() {
+        switch (mEffectId) {
+            case VibrationEffect.EFFECT_CLICK:
+            case VibrationEffect.EFFECT_DOUBLE_CLICK:
+            case VibrationEffect.EFFECT_HEAVY_CLICK:
+            case VibrationEffect.EFFECT_POP:
+            case VibrationEffect.EFFECT_TEXTURE_TICK:
+            case VibrationEffect.EFFECT_THUD:
+            case VibrationEffect.EFFECT_TICK:
+                break;
+            default:
+                int[] ringtones = VibrationEffect.RINGTONES;
+                if (mEffectId < ringtones[0] || mEffectId > ringtones[ringtones.length - 1]) {
+                    throw new IllegalArgumentException(
+                            "Unknown prebaked effect type (value=" + mEffectId + ")");
+                }
+        }
+        if (!isValidEffectStrength(mEffectStrength)) {
+            throw new IllegalArgumentException(
+                    "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
+        }
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (!(o instanceof PrebakedSegment)) {
+            return false;
+        }
+        PrebakedSegment other = (PrebakedSegment) o;
+        return mEffectId == other.mEffectId
+                && mFallback == other.mFallback
+                && mEffectStrength == other.mEffectStrength;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mEffectId, mFallback, mEffectStrength);
+    }
+
+    @Override
+    public String toString() {
+        return "Prebaked{effect=" + VibrationEffect.effectIdToString(mEffectId)
+                + ", strength=" + VibrationEffect.effectStrengthToString(mEffectStrength)
+                + ", fallback=" + mFallback
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(PARCEL_TOKEN_PREBAKED);
+        out.writeInt(mEffectId);
+        out.writeByte((byte) (mFallback ? 1 : 0));
+        out.writeInt(mEffectStrength);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<PrebakedSegment> CREATOR =
+            new Parcelable.Creator<PrebakedSegment>() {
+                @Override
+                public PrebakedSegment createFromParcel(Parcel in) {
+                    // Skip the type token
+                    in.readInt();
+                    return new PrebakedSegment(in);
+                }
+
+                @Override
+                public PrebakedSegment[] newArray(int size) {
+                    return new PrebakedSegment[size];
+                }
+            };
+}
diff --git a/android-34/android/os/vibrator/PrimitiveSegment.java b/android-34/android/os/vibrator/PrimitiveSegment.java
new file mode 100644
index 0000000..cde0ff3
--- /dev/null
+++ b/android-34/android/os/vibrator/PrimitiveSegment.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 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.os.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Representation of {@link VibrationEffectSegment} that plays a primitive vibration effect after a
+ * specified delay and applying a given scale.
+ *
+ * @hide
+ */
+@TestApi
+public final class PrimitiveSegment extends VibrationEffectSegment {
+    private final int mPrimitiveId;
+    private final float mScale;
+    private final int mDelay;
+
+    PrimitiveSegment(@NonNull Parcel in) {
+        this(in.readInt(), in.readFloat(), in.readInt());
+    }
+
+    /** @hide */
+    public PrimitiveSegment(int id, float scale, int delay) {
+        mPrimitiveId = id;
+        mScale = scale;
+        mDelay = delay;
+    }
+
+    public int getPrimitiveId() {
+        return mPrimitiveId;
+    }
+
+    public float getScale() {
+        return mScale;
+    }
+
+    public int getDelay() {
+        return mDelay;
+    }
+
+    @Override
+    public long getDuration() {
+        return -1;
+    }
+
+    /** @hide */
+    @Override
+    public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+        return vibrator.areAllPrimitivesSupported(mPrimitiveId);
+    }
+
+    /** @hide */
+    @Override
+    public boolean isHapticFeedbackCandidate() {
+        return true;
+    }
+
+    /** @hide */
+    @Override
+    public boolean hasNonZeroAmplitude() {
+        // Every primitive plays a vibration with a non-zero amplitude, even at scale == 0.
+        return true;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PrimitiveSegment resolve(int defaultAmplitude) {
+        return this;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PrimitiveSegment scale(float scaleFactor) {
+        return new PrimitiveSegment(mPrimitiveId, VibrationEffect.scale(mScale, scaleFactor),
+                mDelay);
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public PrimitiveSegment applyEffectStrength(int effectStrength) {
+        return this;
+    }
+
+    /** @hide */
+    @Override
+    public void validate() {
+        Preconditions.checkArgumentInRange(mPrimitiveId, VibrationEffect.Composition.PRIMITIVE_NOOP,
+                VibrationEffect.Composition.PRIMITIVE_LOW_TICK, "primitiveId");
+        Preconditions.checkArgumentInRange(mScale, 0f, 1f, "scale");
+        VibrationEffectSegment.checkDurationArgument(mDelay, "delay");
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(PARCEL_TOKEN_PRIMITIVE);
+        dest.writeInt(mPrimitiveId);
+        dest.writeFloat(mScale);
+        dest.writeInt(mDelay);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "Primitive{"
+                + "primitive=" + VibrationEffect.Composition.primitiveToString(mPrimitiveId)
+                + ", scale=" + mScale
+                + ", delay=" + mDelay
+                + '}';
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PrimitiveSegment that = (PrimitiveSegment) o;
+        return mPrimitiveId == that.mPrimitiveId
+                && Float.compare(that.mScale, mScale) == 0
+                && mDelay == that.mDelay;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrimitiveId, mScale, mDelay);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<PrimitiveSegment> CREATOR =
+            new Parcelable.Creator<PrimitiveSegment>() {
+                @Override
+                public PrimitiveSegment createFromParcel(Parcel in) {
+                    // Skip the type token
+                    in.readInt();
+                    return new PrimitiveSegment(in);
+                }
+
+                @Override
+                public PrimitiveSegment[] newArray(int size) {
+                    return new PrimitiveSegment[size];
+                }
+            };
+}
diff --git a/android-34/android/os/vibrator/RampSegment.java b/android-34/android/os/vibrator/RampSegment.java
new file mode 100644
index 0000000..034962a
--- /dev/null
+++ b/android-34/android/os/vibrator/RampSegment.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2021 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.os.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Representation of {@link VibrationEffectSegment} that ramps vibration amplitude and/or frequency
+ * for a specified duration.
+ *
+ * <p>The amplitudes are expressed by float values in the range [0, 1], representing the relative
+ * output acceleration for the vibrator. The frequencies are expressed in hertz by positive finite
+ * float values. The special value zero is used here for an unspecified frequency, and will be
+ * automatically mapped to the device's default vibration frequency (usually the resonant
+ * frequency).
+ *
+ * @hide
+ */
+@TestApi
+public final class RampSegment extends VibrationEffectSegment {
+    private final float mStartAmplitude;
+    private final float mStartFrequencyHz;
+    private final float mEndAmplitude;
+    private final float mEndFrequencyHz;
+    private final int mDuration;
+
+    RampSegment(@NonNull Parcel in) {
+        this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in.readInt());
+    }
+
+    /** @hide */
+    public RampSegment(float startAmplitude, float endAmplitude, float startFrequencyHz,
+            float endFrequencyHz, int duration) {
+        mStartAmplitude = startAmplitude;
+        mEndAmplitude = endAmplitude;
+        mStartFrequencyHz = startFrequencyHz;
+        mEndFrequencyHz = endFrequencyHz;
+        mDuration = duration;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof RampSegment)) {
+            return false;
+        }
+        RampSegment other = (RampSegment) o;
+        return Float.compare(mStartAmplitude, other.mStartAmplitude) == 0
+                && Float.compare(mEndAmplitude, other.mEndAmplitude) == 0
+                && Float.compare(mStartFrequencyHz, other.mStartFrequencyHz) == 0
+                && Float.compare(mEndFrequencyHz, other.mEndFrequencyHz) == 0
+                && mDuration == other.mDuration;
+    }
+
+    public float getStartAmplitude() {
+        return mStartAmplitude;
+    }
+
+    public float getEndAmplitude() {
+        return mEndAmplitude;
+    }
+
+    public float getStartFrequencyHz() {
+        return mStartFrequencyHz;
+    }
+
+    public float getEndFrequencyHz() {
+        return mEndFrequencyHz;
+    }
+
+    @Override
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /** @hide */
+    @Override
+    public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+        boolean areFeaturesSupported = true;
+        // If the start/end frequencies are not the same, require frequency control since we need to
+        // ramp up/down the frequency.
+        if ((mStartFrequencyHz != mEndFrequencyHz)
+                // If there is no frequency ramping, make sure that the one frequency used does not
+                // require frequency control.
+                || frequencyRequiresFrequencyControl(mStartFrequencyHz)) {
+            areFeaturesSupported &= vibrator.hasFrequencyControl();
+        }
+        // If the start/end amplitudes are not the same, require amplitude control since we need to
+        // ramp up/down the amplitude.
+        if ((mStartAmplitude != mEndAmplitude)
+                // If there is no amplitude ramping, make sure that the amplitude used does not
+                // require amplitude control.
+                || amplitudeRequiresAmplitudeControl(mStartAmplitude)) {
+            areFeaturesSupported &= vibrator.hasAmplitudeControl();
+        }
+        return areFeaturesSupported;
+    }
+
+    /** @hide */
+    @Override
+    public boolean isHapticFeedbackCandidate() {
+        return true;
+    }
+
+    /** @hide */
+    @Override
+    public boolean hasNonZeroAmplitude() {
+        return mStartAmplitude > 0 || mEndAmplitude > 0;
+    }
+
+    /** @hide */
+    @Override
+    public void validate() {
+        VibrationEffectSegment.checkFrequencyArgument(mStartFrequencyHz, "startFrequencyHz");
+        VibrationEffectSegment.checkFrequencyArgument(mEndFrequencyHz, "endFrequencyHz");
+        VibrationEffectSegment.checkDurationArgument(mDuration, "duration");
+        Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude");
+        Preconditions.checkArgumentInRange(mEndAmplitude, 0f, 1f, "endAmplitude");
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public RampSegment resolve(int defaultAmplitude) {
+        // Default amplitude is not supported for ramping.
+        return this;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public RampSegment scale(float scaleFactor) {
+        float newStartAmplitude = VibrationEffect.scale(mStartAmplitude, scaleFactor);
+        float newEndAmplitude = VibrationEffect.scale(mEndAmplitude, scaleFactor);
+        if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
+                && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
+            return this;
+        }
+        return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
+                mEndFrequencyHz,
+                mDuration);
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public RampSegment applyEffectStrength(int effectStrength) {
+        return this;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequencyHz, mEndFrequencyHz,
+                mDuration);
+    }
+
+    @Override
+    public String toString() {
+        return "Ramp{startAmplitude=" + mStartAmplitude
+                + ", endAmplitude=" + mEndAmplitude
+                + ", startFrequencyHz=" + mStartFrequencyHz
+                + ", endFrequencyHz=" + mEndFrequencyHz
+                + ", duration=" + mDuration
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(PARCEL_TOKEN_RAMP);
+        out.writeFloat(mStartAmplitude);
+        out.writeFloat(mEndAmplitude);
+        out.writeFloat(mStartFrequencyHz);
+        out.writeFloat(mEndFrequencyHz);
+        out.writeInt(mDuration);
+    }
+
+    @NonNull
+    public static final Creator<RampSegment> CREATOR =
+            new Creator<RampSegment>() {
+                @Override
+                public RampSegment createFromParcel(Parcel in) {
+                    // Skip the type token
+                    in.readInt();
+                    return new RampSegment(in);
+                }
+
+                @Override
+                public RampSegment[] newArray(int size) {
+                    return new RampSegment[size];
+                }
+            };
+}
diff --git a/android-34/android/os/vibrator/StepSegment.java b/android-34/android/os/vibrator/StepSegment.java
new file mode 100644
index 0000000..115a66c
--- /dev/null
+++ b/android-34/android/os/vibrator/StepSegment.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2021 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.os.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Representation of {@link VibrationEffectSegment} that holds a fixed vibration amplitude and
+ * frequency for a specified duration.
+ *
+ * <p>The amplitude is expressed by a float value in the range [0, 1], representing the relative
+ * output acceleration for the vibrator. The frequency is expressed in hertz by a positive finite
+ * float value. The special value zero is used here for an unspecified frequency, and will be
+ * automatically mapped to the device's default vibration frequency (usually the resonant
+ * frequency).
+ *
+ * @hide
+ */
+@TestApi
+public final class StepSegment extends VibrationEffectSegment {
+    private final float mAmplitude;
+    private final float mFrequencyHz;
+    private final int mDuration;
+
+    StepSegment(@NonNull Parcel in) {
+        this(in.readFloat(), in.readFloat(), in.readInt());
+    }
+
+    /** @hide */
+    public StepSegment(float amplitude, float frequencyHz, int duration) {
+        mAmplitude = amplitude;
+        mFrequencyHz = frequencyHz;
+        mDuration = duration;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof StepSegment)) {
+            return false;
+        }
+        StepSegment other = (StepSegment) o;
+        return Float.compare(mAmplitude, other.mAmplitude) == 0
+                && Float.compare(mFrequencyHz, other.mFrequencyHz) == 0
+                && mDuration == other.mDuration;
+    }
+
+    public float getAmplitude() {
+        return mAmplitude;
+    }
+
+    public float getFrequencyHz() {
+        return mFrequencyHz;
+    }
+
+    @Override
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /** @hide */
+    @Override
+    public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+        boolean areFeaturesSupported = true;
+        if (frequencyRequiresFrequencyControl(mFrequencyHz)) {
+            areFeaturesSupported &= vibrator.hasFrequencyControl();
+        }
+        if (amplitudeRequiresAmplitudeControl(mAmplitude)) {
+            areFeaturesSupported &= vibrator.hasAmplitudeControl();
+        }
+        return areFeaturesSupported;
+    }
+
+    /** @hide */
+    @Override
+    public boolean isHapticFeedbackCandidate() {
+        return true;
+    }
+
+    /** @hide */
+    @Override
+    public boolean hasNonZeroAmplitude() {
+        // DEFAULT_AMPLITUDE == -1 is still a non-zero amplitude that will be resolved later.
+        return Float.compare(mAmplitude, 0) != 0;
+    }
+
+    /** @hide */
+    @Override
+    public void validate() {
+        VibrationEffectSegment.checkFrequencyArgument(mFrequencyHz, "frequencyHz");
+        VibrationEffectSegment.checkDurationArgument(mDuration, "duration");
+        if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
+            Preconditions.checkArgumentInRange(mAmplitude, 0f, 1f, "amplitude");
+        }
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public StepSegment resolve(int defaultAmplitude) {
+        if (defaultAmplitude > VibrationEffect.MAX_AMPLITUDE || defaultAmplitude <= 0) {
+            throw new IllegalArgumentException(
+                    "amplitude must be between 1 and 255 inclusive (amplitude="
+                            + defaultAmplitude + ")");
+        }
+        if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
+            return this;
+        }
+        return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE,
+                mFrequencyHz,
+                mDuration);
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public StepSegment scale(float scaleFactor) {
+        if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
+            return this;
+        }
+        return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz,
+                mDuration);
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public StepSegment applyEffectStrength(int effectStrength) {
+        return this;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAmplitude, mFrequencyHz, mDuration);
+    }
+
+    @Override
+    public String toString() {
+        return "Step{amplitude=" + mAmplitude
+                + ", frequencyHz=" + mFrequencyHz
+                + ", duration=" + mDuration
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(PARCEL_TOKEN_STEP);
+        out.writeFloat(mAmplitude);
+        out.writeFloat(mFrequencyHz);
+        out.writeInt(mDuration);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<StepSegment> CREATOR =
+            new Parcelable.Creator<StepSegment>() {
+                @Override
+                public StepSegment createFromParcel(Parcel in) {
+                    // Skip the type token
+                    in.readInt();
+                    return new StepSegment(in);
+                }
+
+                @Override
+                public StepSegment[] newArray(int size) {
+                    return new StepSegment[size];
+                }
+            };
+}
diff --git a/android-34/android/os/vibrator/VibrationConfig.java b/android-34/android/os/vibrator/VibrationConfig.java
new file mode 100644
index 0000000..4a61472
--- /dev/null
+++ b/android-34/android/os/vibrator/VibrationConfig.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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.os.vibrator;
+
+import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_MEDIA;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationAttributes;
+import android.os.Vibrator;
+import android.os.Vibrator.VibrationIntensity;
+
+/**
+ * List of device-specific internal vibration configuration loaded from platform config.xml.
+ *
+ * <p>This should not be public, but some individual values are exposed by {@link Vibrator} by
+ * hidden methods, made available to Settings, SysUI and other platform client code. They can also
+ * be individually exposed with the necessary permissions by the {@link Vibrator} service.
+ *
+ * @hide
+ */
+public class VibrationConfig {
+
+    // TODO(b/191150049): move these to vibrator static config file
+    private final float mHapticChannelMaxVibrationAmplitude;
+    private final int mRampStepDurationMs;
+    private final int mRampDownDurationMs;
+
+    @VibrationIntensity
+    private final int mDefaultAlarmVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultHapticFeedbackIntensity;
+    @VibrationIntensity
+    private final int mDefaultMediaVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultNotificationVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultRingVibrationIntensity;
+
+    /** @hide */
+    public VibrationConfig(@Nullable Resources resources) {
+        mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
+                com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
+        mRampDownDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0);
+        mRampStepDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationWaveformRampStepDuration, 0);
+
+        mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
+        mDefaultHapticFeedbackIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
+        mDefaultMediaVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultMediaVibrationIntensity);
+        mDefaultNotificationVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
+        mDefaultRingVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultRingVibrationIntensity);
+    }
+
+    @VibrationIntensity
+    private static int loadDefaultIntensity(@Nullable Resources res, int resId) {
+        int defaultIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        int value = loadInteger(res, resId, defaultIntensity);
+        if (value < Vibrator.VIBRATION_INTENSITY_OFF || value > Vibrator.VIBRATION_INTENSITY_HIGH) {
+            return defaultIntensity;
+        }
+        return value;
+    }
+
+    private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) {
+        return res != null ? res.getFloat(resId) : defaultValue;
+    }
+
+    private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) {
+        return res != null ? res.getInteger(resId) : defaultValue;
+    }
+
+    /**
+     * Return the maximum amplitude the vibrator can play using the audio haptic channels.
+     *
+     * @return a positive value representing the maximum absolute value the device can play signals
+     * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown.
+     */
+    public float getHapticChannelMaximumAmplitude() {
+        if (mHapticChannelMaxVibrationAmplitude <= 0) {
+            return Float.NaN;
+        }
+        return mHapticChannelMaxVibrationAmplitude;
+    }
+
+    /**
+     * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator
+     * when a vibration is cancelled or finished at non-zero amplitude.
+     */
+    public int getRampDownDurationMs() {
+        if (mRampDownDurationMs < 0) {
+            return 0;
+        }
+        return mRampDownDurationMs;
+    }
+
+    /**
+     * The duration, in milliseconds, that should be applied to convert vibration effect's
+     * {@link android.os.vibrator.RampSegment} to a {@link android.os.vibrator.StepSegment} on
+     * devices without PWLE support.
+     */
+    public int getRampStepDurationMs() {
+        if (mRampStepDurationMs < 0) {
+            return 0;
+        }
+        return mRampStepDurationMs;
+    }
+
+    /** Get the default vibration intensity for given usage. */
+    @VibrationIntensity
+    public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
+        switch (usage) {
+            case USAGE_ALARM:
+                return mDefaultAlarmVibrationIntensity;
+            case USAGE_NOTIFICATION:
+            case USAGE_COMMUNICATION_REQUEST:
+                return mDefaultNotificationVibrationIntensity;
+            case USAGE_RINGTONE:
+                return mDefaultRingVibrationIntensity;
+            case USAGE_TOUCH:
+            case USAGE_HARDWARE_FEEDBACK:
+            case USAGE_PHYSICAL_EMULATION:
+            case USAGE_ACCESSIBILITY:
+                return mDefaultHapticFeedbackIntensity;
+            case USAGE_MEDIA:
+            case USAGE_UNKNOWN:
+                // fall through
+            default:
+                return mDefaultMediaVibrationIntensity;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "VibrationConfig{"
+                + "mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+                + ", mRampStepDurationMs=" + mRampStepDurationMs
+                + ", mRampDownDurationMs=" + mRampDownDurationMs
+                + ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity
+                + ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity
+                + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity
+                + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
+                + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
+                + "}";
+    }
+}
diff --git a/android-34/android/os/vibrator/VibrationEffectSegment.java b/android-34/android/os/vibrator/VibrationEffectSegment.java
new file mode 100644
index 0000000..75a055f
--- /dev/null
+++ b/android-34/android/os/vibrator/VibrationEffectSegment.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2021 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.os.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+
+/**
+ * Representation of a single segment of a {@link VibrationEffect}.
+ *
+ * <p>Vibration effects are represented as a sequence of segments that describes how vibration
+ * amplitude and frequency changes over time. Segments can be described as one of the following:
+ *
+ * <ol>
+ *     <li>A predefined vibration effect;
+ *     <li>A composable effect primitive;
+ *     <li>Fixed amplitude and frequency values to be held for a specified duration;
+ *     <li>Pairs of amplitude and frequency values to be ramped to for a specified duration;
+ * </ol>
+ *
+ * @hide
+ */
+@TestApi
+@SuppressWarnings({"ParcelNotFinal", "ParcelCreator"}) // Parcel only extended here.
+public abstract class VibrationEffectSegment implements Parcelable {
+    static final int PARCEL_TOKEN_PREBAKED = 1;
+    static final int PARCEL_TOKEN_PRIMITIVE = 2;
+    static final int PARCEL_TOKEN_STEP = 3;
+    static final int PARCEL_TOKEN_RAMP = 4;
+
+    /** Prevent subclassing from outside of this package */
+    VibrationEffectSegment() {
+    }
+
+    /**
+     * Gets the estimated duration of the segment in milliseconds.
+     *
+     * <p>For segments with an unknown duration (e.g. prebaked or primitive effects where the length
+     * is device and potentially run-time dependent), this returns -1.
+     */
+    public abstract long getDuration();
+
+   /**
+     * Checks if a given {@link Vibrator} can play this segment as intended. See
+     * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about
+     * what counts as supported by a vibrator, and what counts as not.
+     *
+     * @hide
+     */
+    public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+
+    /**
+     * Returns true if this segment could be a haptic feedback effect candidate.
+     *
+     * @see VibrationEffect#isHapticFeedbackCandidate()
+     * @hide
+     */
+    public abstract boolean isHapticFeedbackCandidate();
+
+    /**
+     * Returns true if this segment plays at a non-zero amplitude at some point.
+     *
+     * @hide
+     */
+    public abstract boolean hasNonZeroAmplitude();
+
+    /**
+     * Validates the segment, throwing exceptions if any parameter is invalid.
+     *
+     * @hide
+     */
+    public abstract void validate();
+
+    /**
+     * Resolves amplitudes set to {@link VibrationEffect#DEFAULT_AMPLITUDE}.
+     *
+     * <p>This might fail with {@link IllegalArgumentException} if value is non-positive or larger
+     * than {@link VibrationEffect#MAX_AMPLITUDE}.
+     *
+     * @hide
+     */
+    @NonNull
+    public abstract <T extends VibrationEffectSegment> T resolve(int defaultAmplitude);
+
+    /**
+     * Scale the segment intensity with the given factor.
+     *
+     * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+     *                    scale down the intensity, values larger than 1 will scale up
+     *
+     * @hide
+     */
+    @NonNull
+    public abstract <T extends VibrationEffectSegment> T scale(float scaleFactor);
+
+    /**
+     * Applies given effect strength to prebaked effects.
+     *
+     * @param effectStrength new effect strength to be applied, one of
+     *                       VibrationEffect.EFFECT_STRENGTH_*.
+     *
+     * @hide
+     */
+    @NonNull
+    public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength);
+
+    /**
+     * Checks the given frequency argument is valid to represent a vibration effect frequency in
+     * hertz, i.e. a finite non-negative value.
+     *
+     * @param value the frequency argument value to be checked
+     * @param name the argument name for the error message.
+     *
+     * @hide
+     */
+    public static void checkFrequencyArgument(float value, @NonNull String name) {
+        // Similar to combining Preconditions checkArgumentFinite + checkArgumentNonnegative,
+        // but this implementation doesn't create the error message unless a check fail.
+        if (Float.isNaN(value)) {
+            throw new IllegalArgumentException(name + " must not be NaN");
+        }
+        if (Float.isInfinite(value)) {
+            throw new IllegalArgumentException(name + " must not be infinite");
+        }
+        if (value < 0) {
+            throw new IllegalArgumentException(name + " must be >= 0, got " + value);
+        }
+    }
+
+    /**
+     * Checks the given duration argument is valid, i.e. a non-negative value.
+     *
+     * @param value the duration value to be checked
+     * @param name the argument name for the error message.
+     *
+     * @hide
+     */
+    public static void checkDurationArgument(long value, @NonNull String name) {
+        if (value < 0) {
+            throw new IllegalArgumentException(name + " must be >= 0, got " + value);
+        }
+    }
+
+    /**
+     * Helper method to check if an amplitude requires a vibrator to have amplitude control to play.
+     *
+     * @hide
+     */
+    protected static boolean amplitudeRequiresAmplitudeControl(float amplitude) {
+        return (amplitude != 0)
+                && (amplitude != 1)
+                && (amplitude != VibrationEffect.DEFAULT_AMPLITUDE);
+    }
+
+    /**
+     * Helper method to check if a frequency requires a vibrator to have frequency control to play.
+     *
+     * @hide
+     */
+    protected static boolean frequencyRequiresFrequencyControl(float frequency) {
+        // Anything other than the default frequency value (represented with "0") requires frequency
+        // control.
+        return frequency != 0;
+    }
+
+    @NonNull
+    public static final Creator<VibrationEffectSegment> CREATOR =
+            new Creator<VibrationEffectSegment>() {
+                @Override
+                public VibrationEffectSegment createFromParcel(Parcel in) {
+                    switch (in.readInt()) {
+                        case PARCEL_TOKEN_STEP:
+                            return new StepSegment(in);
+                        case PARCEL_TOKEN_RAMP:
+                            return new RampSegment(in);
+                        case PARCEL_TOKEN_PREBAKED:
+                            return new PrebakedSegment(in);
+                        case PARCEL_TOKEN_PRIMITIVE:
+                            return new PrimitiveSegment(in);
+                        default:
+                            throw new IllegalStateException(
+                                    "Unexpected vibration event type token in parcel.");
+                    }
+                }
+
+                @Override
+                public VibrationEffectSegment[] newArray(int size) {
+                    return new VibrationEffectSegment[size];
+                }
+            };
+}
diff --git a/android-34/android/os/vibrator/VibratorFrequencyProfile.java b/android-34/android/os/vibrator/VibratorFrequencyProfile.java
new file mode 100644
index 0000000..8392940
--- /dev/null
+++ b/android-34/android/os/vibrator/VibratorFrequencyProfile.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 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.os.vibrator;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.VibratorInfo;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Describes the output of a {@link android.os.Vibrator} for different vibration frequencies.
+ *
+ * <p>The profile contains the minimum and maximum supported vibration frequencies, if the device
+ * supports independent frequency control.
+ *
+ * <p>It also describes the relative output acceleration of a vibration at different supported
+ * frequencies. The acceleration is defined by a relative amplitude value between 0 and 1,
+ * inclusive, where 0 represents the vibrator off state and 1 represents the maximum output
+ * acceleration that the vibrator can reach across all supported frequencies.
+ *
+ * <p>The measurements are returned as an array of uniformly distributed amplitude values for
+ * frequencies between the minimum and maximum supported ones. The measurement interval is the
+ * frequency increment between each pair of amplitude values.
+ *
+ * <p>Vibrators without independent frequency control do not have a frequency profile.
+ * @hide
+ */
+@TestApi
+public final class VibratorFrequencyProfile {
+
+    private final VibratorInfo.FrequencyProfile mFrequencyProfile;
+
+    /** @hide */
+    public VibratorFrequencyProfile(@NonNull VibratorInfo.FrequencyProfile frequencyProfile) {
+        Preconditions.checkArgument(!frequencyProfile.isEmpty(),
+                "Frequency profile must have a non-empty frequency range");
+        mFrequencyProfile = frequencyProfile;
+    }
+
+    /**
+     * Measurements of the maximum relative amplitude the vibrator can achieve for each supported
+     * frequency.
+     *
+     * <p>The frequency of a measurement is determined as:
+     *
+     * {@code getMinFrequency() + measurementIndex * getMaxAmplitudeMeasurementInterval()}
+     *
+     * <p>The returned list will not be empty, and will have entries representing frequencies from
+     * {@link #getMinFrequency()} to {@link #getMaxFrequency()}, inclusive.
+     *
+     * @return Array of maximum relative amplitude measurements.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @FloatRange(from = 0, to = 1)
+    public float[] getMaxAmplitudeMeasurements() {
+        // VibratorInfo getters always return a copy or clone of the data objects.
+        return mFrequencyProfile.getMaxAmplitudes();
+    }
+
+    /**
+     * Gets the frequency interval used to measure the maximum relative amplitudes.
+     *
+     * @return the frequency interval used for the measurement, in hertz.
+     * @hide
+     */
+    @TestApi
+    public float getMaxAmplitudeMeasurementInterval() {
+        return mFrequencyProfile.getFrequencyResolutionHz();
+    }
+
+    /**
+     * Gets the minimum frequency supported by the vibrator.
+     *
+     * @return the minimum frequency supported by the vibrator, in hertz.
+     * @hide
+     */
+    @TestApi
+    public float getMinFrequency() {
+        return mFrequencyProfile.getFrequencyRangeHz().getLower();
+    }
+
+    /**
+     * Gets the maximum frequency supported by the vibrator.
+     *
+     * @return the maximum frequency supported by the vibrator, in hertz.
+     * @hide
+     */
+    @TestApi
+    public float getMaxFrequency() {
+        return mFrequencyProfile.getFrequencyRangeHz().getUpper();
+    }
+}